Как удалить файл из списка файлов

111

Я создаю веб-приложение с перетаскиванием и загрузкой с использованием HTML5, и я перетаскиваю файлы в div и, конечно же, получаю объект dataTransfer, который дает мне FileList .

Теперь я хочу удалить некоторые файлы, но не знаю, как это сделать, и возможно ли это вообще.

Желательно просто удалить их из FileList; Мне они не нужны. Но если это невозможно, следует ли мне вместо этого писать чеки в коде, который взаимодействует с FileList? Это кажется громоздким.

Heilemann
источник
Просто любопытно: зачем ты это хочешь? Почему вы говорите «Они мне не нужны» о (некоторых) файлах, выбранных пользователем?
Marcel Korpel 01
24
Вероятно, это больше для того, чтобы пользователь мог удалить файлы перед загрузкой. Если вы изначально выбрали 20, а затем решили, что на самом деле не хотите загружать 14-й, то вы не можете просто удалить его, вам придется начинать все сначала (что немного неудобно). Я думаю, что создание FileList только для чтения - это плохая оплошность, если только я не вижу каких-либо последствий для безопасности.
Rafael
Это проблемы с безопасностью при удалении файлов из входного FileList напрямую, но вы можете клонировать этот FileList сразу после закрытия диалогового окна загрузки файлов, а затем изменить этот клон и использовать его при публикации через ajax
alex_1948511

Ответы:

150

Если вы хотите удалить только несколько выбранных файлов: вы не можете. API Файл рабочего проекта вы связаны с содержит примечание:

HTMLInputElementИнтерфейс [HTML5] имеет только для чтения FileList атрибут, [...]
[курсив мой]

Немного прочитав рабочий проект HTML 5, я наткнулся на API общих inputэлементов . Похоже, вы можете удалить весь список файлов, установив для valueсвойства inputобъекта пустую строку, например:

document.getElementById('multifile').value = "";

Кстати, статья Использование файлов из веб-приложений также может быть интересна.

Марсель Корпель
источник
1
Обратите внимание, что атрибут только для чтения не означает, что вы не можете изменить объект, на который он указывает. Вы можете манипулировать FileList (если бы это было возможно), это просто означает, что вы не можете назначить ему новый FileList.
Робин Берджон
1
@RobinBerjon Chrome, похоже, игнорирует атрибут «readonly», в то время как FireFox не разрешает операции записи. К сожалению, ваше предложение просто манипулировать FileList не работает и в FireFox.
borisdiakur
1
lengthДумаю, только для чтения. Я пытаюсь удалить элемент с помощью splice, в Chrome это не удается.
zhiyelee 04
Есть ли возможность добавить?
уличный
1
@streetlight Это было бы огромной уязвимостью безопасности, если бы владелец сайта мог определять, какие файлы загружать с компьютера пользователя.
Марсель Корпель,
30

На этот вопрос уже отмечен ответ, но я хотел бы поделиться некоторой информацией, которая может помочь другим в использовании FileList.

Было бы удобно рассматривать FileList как массив, но такие методы, как sort, shift, pop и slice, не работают. Как предлагали другие, вы можете скопировать FileList в массив. Однако вместо использования цикла есть простое однострочное решение для обработки этого преобразования.

 // fileDialog.files is a FileList 

 var fileBuffer=[];

 // append the file list to an array
 Array.prototype.push.apply( fileBuffer, fileDialog.files ); // <-- here

 // And now you may manipulated the result as required

 // shift an item off the array
 var file = fileBuffer.shift(0,1);  // <-- works as expected
 console.info( file.name + ", " + file.size + ", " + file.type );

 // sort files by size
 fileBuffer.sort(function(a,b) {
    return a.size > b.size ? 1 : a.size < b.size ? -1 : 0;
 });

Проверено нормально в FF, Chrome и IE10 +

Роберто
источник
4
Array.from(fileDialog.files)проще
Мухаммад Умер
1
@Muhammad Umer - Спасибо, я согласен, что это проще, и он указан как альтернативный ответ. Тем не менее, это зависит от того, какие браузеры необходимо поддерживать и требуется ли им заполнение для использования Array.from (). См .: stackoverflow.com/a/36810954/943435
Роберто
Как вы на самом деле изменяете FileList? Назначить этот новый массив входу fileDialog.files = fileBuffer ?
eozzy
@ 3zzy - Изменить FileList можно, но только в современных браузерах. Подробнее см. В этом SO-вопросе: stackoverflow.com/a/47522812/943435
Роберто
22

Если вы ориентируетесь на вечнозеленые браузеры (Chrome, Firefox, Edge, но также работает в Safari 9+) или можете позволить себе полифил, вы можете превратить FileList в массив, используя Array.from()следующее:

let fileArray = Array.from(fileList);

Тогда с массивом Files легко работать, как с любым другим массивом.

adlr0
источник
Отлично! Вы знаете, как насчет поддержки IE? А может ссылку на полифилл можно поделиться?
Сергей Матрунчик
Я не пробовал, но это первый результат Google;) github.com/mathiasbynens/Array.from
adlr0
Это только позволит вам fileArrayне справиться fileList.
VipinKundal
12

Поскольку мы находимся в сфере HTML5, это мое решение. Суть в том, что вы отправляете файлы в массив, а не оставляете их в списке файлов, а затем, используя XHR2, отправляете файлы в объект FormData. Пример ниже.

Node.prototype.replaceWith = function(node)
{
    this.parentNode.replaceChild(node, this);
};
if(window.File && window.FileList)
{
    var topicForm = document.getElementById("yourForm");
    topicForm.fileZone = document.getElementById("fileDropZoneElement");
    topicForm.fileZone.files = new Array();
    topicForm.fileZone.inputWindow = document.createElement("input");
    topicForm.fileZone.inputWindow.setAttribute("type", "file");
    topicForm.fileZone.inputWindow.setAttribute("multiple", "multiple");
    topicForm.onsubmit = function(event)
    {
        var request = new XMLHttpRequest();
        if(request.upload)
        {
            event.preventDefault();
            topicForm.ajax.value = "true";
            request.upload.onprogress = function(event)
            {
                var progress = event.loaded.toString() + " bytes transfered.";
                if(event.lengthComputable)
                progress = Math.round(event.loaded / event.total * 100).toString() + "%";
                topicForm.fileZone.innerHTML = progress.toString();
            };
            request.onload = function(event)
            {
                response = JSON.parse(request.responseText);
                // Handle the response here.
            };
            request.open(topicForm.method, topicForm.getAttribute("action"), true);
            var data = new FormData(topicForm);
            for(var i = 0, file; file = topicForm.fileZone.files[i]; i++)
                data.append("file" + i.toString(), file);
            request.send(data);
        }
    };
    topicForm.fileZone.firstChild.replaceWith(document.createTextNode("Drop files or click here."));
    var handleFiles = function(files)
    {
        for(var i = 0, file; file = files[i]; i++)
            topicForm.fileZone.files.push(file);
    };
    topicForm.fileZone.ondrop = function(event)
    {
        event.stopPropagation();
        event.preventDefault();
        handleFiles(event.dataTransfer.files);
    };
    topicForm.fileZone.inputWindow.onchange = function(event)
    {
        handleFiles(topicForm.fileZone.inputWindow.files);
    };
    topicForm.fileZone.ondragover = function(event)
    {
        event.stopPropagation();
        event.preventDefault();
    };
    topicForm.fileZone.onclick = function()
    {
        topicForm.fileZone.inputWindow.focus();
        topicForm.fileZone.inputWindow.click();
    };
}
else
    topicForm.fileZone.firstChild.replaceWith(document.createTextNode("It's time to update your browser."));
Джошуа В.
источник
Думаю, единственный способ - это ajax?
Мухаммад Умер
10

Я нашел очень быстрое и короткое решение для этого. Протестировано во многих популярных браузерах (Chrome, Firefox, Safari);

Во-первых, вам нужно преобразовать FileList в массив

var newFileList = Array.from(event.target.files);

чтобы удалить конкретный элемент, используйте это

newFileList.splice(index,1);
MeVimalkumar
источник
12
Вы создали новую переменную, event.target.filesкоторая не связана с вводом, поэтому она не может ничего изменить, кроме вашей локальной переменной ..
Максимс Китаевс
6

Я знаю, что это старый вопрос, но он занимает высокие позиции в поисковых системах по этому поводу.

свойства в объекте FileList нельзя удалить, но, по крайней мере, в Firefox их можно изменить . Мое решение этой проблемы состояло в том, чтобы добавить свойство IsValid=trueк тем файлам, которые прошли проверку, и IsValid=falseк тем, которые не прошли .

затем я просто просматриваю список, чтобы убедиться, что IsValid=trueв FormData добавлены только свойства с .

А. Ричардс
источник
formdata, значит, вы отправляете их через ajax?
Мухаммад Умер
1

Возможно, есть более элегантный способ сделать это, но вот мое решение. С помощью JQuery

fileEle.value = "";
var parEle = $(fileEle).parent();
var newEle = $(fileEle).clone()
$(fileEle).remove();
parEle.append(newEle);

В основном вы очищаете значение ввода. Клонируйте его и поместите клон вместо старого.

Николас Андерсон
источник
1

Это ненадолго, но у меня была та же проблема, которую я решил таким образом. В моем случае я загружал файлы через запрос XMLHttp, поэтому я смог опубликовать клонированные данные FileList с помощью добавления formdata. Функциональность заключается в том, что вы можете перетаскивать или выбирать несколько файлов столько раз, сколько хотите (повторный выбор файлов не приведет к сбросу клонированного списка файлов), удалить любой файл, который вы хотите, из (клонированного) списка файлов и отправить через xmlhttprequest все, что было оставил там. Вот что я сделал. Это мой первый пост, поэтому код немного запутан. Сожалею. А, и мне пришлось использовать jQuery вместо $, как это было в скрипте Joomla.

// some global variables
var clon = {};  // will be my FileList clone
var removedkeys = 0; // removed keys counter for later processing the request
var NextId = 0; // counter to add entries to the clone and not replace existing ones

jQuery(document).ready(function(){
    jQuery("#form input").change(function () {

    // making the clone
    var curFiles = this.files;
    // temporary object clone before copying info to the clone
    var temparr = jQuery.extend(true, {}, curFiles);
    // delete unnecessary FileList keys that were cloned
    delete temparr["length"];
    delete temparr["item"];

    if (Object.keys(clon).length === 0){
       jQuery.extend(true, clon, temparr);
    }else{
       var keysArr = Object.keys(clon);
       NextId = Math.max.apply(null, keysArr)+1; // FileList keys are numbers
       if (NextId < curFiles.length){ // a bug I found and had to solve for not replacing my temparr keys...
          NextId = curFiles.length;
       }
       for (var key in temparr) { // I have to rename new entries for not overwriting existing keys in clon
          if (temparr.hasOwnProperty(key)) {
             temparr[NextId] = temparr[key];
             delete temparr[key];
                // meter aca los cambios de id en los html tags con el nuevo NextId
                NextId++;
          }
       } 
       jQuery.extend(true, clon, temparr); // copy new entries to clon
    }

// modifying the html file list display

if (NextId === 0){
    jQuery("#filelist").html("");
    for(var i=0; i<curFiles.length; i++) {
        var f = curFiles[i];
        jQuery("#filelist").append("<p id=\"file"+i+"\" style=\'margin-bottom: 3px!important;\'>" + f.name + "<a style=\"float:right;cursor:pointer;\" onclick=\"BorrarFile("+i+")\">x</a></p>"); // the function BorrarFile will handle file deletion from the clone by file id
    }
}else{
    for(var i=0; i<curFiles.length; i++) {
        var f = curFiles[i];
        jQuery("#filelist").append("<p id=\"file"+(i+NextId-curFiles.length)+"\" style=\'margin-bottom: 3px!important;\'>" + f.name + "<a style=\"float:right;cursor:pointer;\" onclick=\"BorrarFile("+(i+NextId-curFiles.length)+")\">x</a></p>"); // yeap, i+NextId-curFiles.length actually gets it right
    }        
}
// update the total files count wherever you want
jQuery("#form p").text(Object.keys(clon).length + " file(s) selected");
    });
});

function BorrarFile(id){ // handling file deletion from clone
    jQuery("#file"+id).remove(); // remove the html filelist element
    delete clon[id]; // delete the entry
    removedkeys++; // add to removed keys counter
    if (Object.keys(clon).length === 0){
        jQuery("#form p").text(Object.keys(clon).length + " file(s) selected");
        jQuery("#fileToUpload").val(""); // I had to reset the form file input for my form check function before submission. Else it would send even though my clone was empty
    }else{
        jQuery("#form p").text(Object.keys(clon).length + " file(s) selected");
    }
}
// now my form check function

function check(){
    if( document.getElementById("fileToUpload").files.length == 0 ){
        alert("No file selected");
        return false;
    }else{
        var _validFileExtensions = [".pdf", ".PDF"]; // I wanted pdf files
        // retrieve input files
        var arrInputs = clon;

       // validating files
       for (var i = 0; i < Object.keys(arrInputs).length+removedkeys; i++) {
         if (typeof arrInputs[i]!="undefined"){
           var oInput = arrInputs[i];
           if (oInput.type == "application/pdf") {
               var sFileName = oInput.name;
               if (sFileName.length > 0) {
                   var blnValid = false;
                   for (var j = 0; j < _validFileExtensions.length; j++) {
                     var sCurExtension = _validFileExtensions[j];
                     if (sFileName.substr(sFileName.length - sCurExtension.length, sCurExtension.length).toLowerCase() == sCurExtension.toLowerCase()) {
                       blnValid = true;
                       break;
                     }
                   }
                  if (!blnValid) {
                    alert("Sorry, " + sFileName + " is invalid, allowed extensions are: " + _validFileExtensions.join(", "));
                    return false;
                  }
              }
           }else{
           alert("Sorry, " + arrInputs[0].name + " is invalid, allowed extensions are: " + _validFileExtensions.join(" or "));
           return false;
           }
         }
       }

    // proceed with the data appending and submission
    // here some hidden input values i had previously set. Now retrieving them for submission. My form wasn't actually even a form...
    var fecha = jQuery("#fecha").val();
    var vendor = jQuery("#vendor").val();
    var sku = jQuery("#sku").val();
    // create the formdata object
    var formData = new FormData();
    formData.append("fecha", fecha);
    formData.append("vendor", encodeURI(vendor));
    formData.append("sku", sku);
    // now appending the clone file data (finally!)
    var fila = clon; // i just did this because I had already written the following using the "fila" object, so I copy my clone again
    // the interesting part. As entries in my clone object aren't consecutive numbers I cannot iterate normally, so I came up with the following idea
    for (i = 0; i < Object.keys(fila).length+removedkeys; i++) { 
        if(typeof fila[i]!="undefined"){
            formData.append("fileToUpload[]", fila[i]); // VERY IMPORTANT the formdata key for the files HAS to be an array. It will be later retrieved as $_FILES['fileToUpload']['temp_name'][i]
        }
    }
    jQuery("#submitbtn").fadeOut("slow"); // remove the upload btn so it can't be used again
    jQuery("#drag").html(""); // clearing the output message element
    // start the request
    var xhttp = new XMLHttpRequest();
    xhttp.addEventListener("progress", function(e) {
            var done = e.position || e.loaded, total = e.totalSize || e.total;
        }, false);
        if ( xhttp.upload ) {
            xhttp.upload.onprogress = function(e) {
                var done = e.position || e.loaded, total = e.totalSize || e.total;
                var percent = done / total;
                jQuery("#drag").html(Math.round(percent * 100) + "%");
            };
        }
      xhttp.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {
         var respuesta = this.responseText;
         jQuery("#drag").html(respuesta);
        }
      };
      xhttp.open("POST", "your_upload_handler.php", true);  
      xhttp.send(formData);
    return true;
    }
};

Теперь о HTML и стилях для этого. Я новичок, но все это на самом деле сработало для меня, и мне потребовалось время, чтобы понять это.

<div id="form" class="formpos">
<!--    Select the pdf to upload:-->
  <input type="file" name="fileToUpload[]" id="fileToUpload" accept="application/pdf" multiple>
  <div><p id="drag">Drop your files here or click to select them</p>
  </div>
  <button id="submitbtn" onclick="return check()" >Upload</button>
// these inputs are passed with different names on the formdata. Be aware of that
// I was echoing this, so that's why I use the single quote for php variables
  <input type="hidden" id="fecha" name="fecha_copy" value="'.$fecha.'" />
  <input type="hidden" id="vendor" name="vendorname" value="'.$vendor.'" />
  <input type="hidden" id="sku" name="sku" value="'.$sku.'"" />
</div>
<h1 style="width: 500px!important;margin:20px auto 0px!important;font-size:24px!important;">File list:</h1>
<div id="filelist" style="width: 500px!important;margin:10px auto 0px!important;">Nothing selected yet</div>

Стили для этого. Мне пришлось отметить некоторые из них! Важные, чтобы переопределить поведение Joomla.

.formpos{
  width: 500px;
  height: 200px;
  border: 4px dashed #999;
  margin: 30px auto 100px;
 }
.formpos  p{
  text-align: center!important;
  padding: 80px 30px 0px;
  color: #000;
}
.formpos  div{
  width: 100%!important;
  height: 100%!important;
  text-align: center!important;
  margin-bottom: 30px!important;
}
.formpos input{
  position: absolute!important;
  margin: 0!important;
  padding: 0!important;
  width: 500px!important;
  height: 200px!important;
  outline: none!important;
  opacity: 0!important;
}
.formpos button{
  margin: 0;
  color: #fff;
  background: #16a085;
  border: none;
  width: 508px;
  height: 35px;
  margin-left: -4px;
  border-radius: 4px;
  transition: all .2s ease;
  outline: none;
}
.formpos button:hover{
  background: #149174;
  color: #0C5645;
}
.formpos button:active{
  border:0;
}

Надеюсь, это поможет.

Эрик
источник
1

Спасибо @Nicholas Anderson, просто и прямо, вот ваш код, примененный и работающий над моим кодом с использованием jquery.

HTML.

<input class="rangelog btn border-aero" id="file_fr" name="file_fr[]" multiple type="file" placeholder="{$labels_helpfiles_placeholder_file}">
<span style="cursor: pointer; cursor: hand;" onclick="cleanInputs($('#file_fr'))"><i class="fa fa-trash"></i> Empty chosen files</span>

КОД JS

   function cleanInputs(fileEle){
    $(fileEle).val("");
    var parEle = $(fileEle).parent();
    var newEle = $(fileEle).clone()
    $(fileEle).remove();
    $(parEle).prepend(newEle);
}
Султанос
источник
0

Если вам посчастливилось отправить почтовый запрос в базу данных с файлами, и у вас есть файлы, которые вы хотите отправить, в вашей DOM

вы можете просто проверить, присутствует ли файл в списке файлов в вашей DOM, и, конечно, если это не так, вы просто не отправляете этот элемент в de DB.

Neku80
источник
-1

Вы можете создать массив и использовать его вместо списка файлов, доступного только для чтения.

var myReadWriteList = new Array();
// user selects files later...
// then as soon as convenient... 
myReadWriteList = FileListReadOnly;

После этого выполняйте загрузку по вашему списку, а не по встроенному списку. Я не уверен в контексте, в котором вы работаете, но я работаю с плагином jquery, который я нашел, и мне нужно было взять исходный код плагина и поместить его на страницу с помощью <script>тегов. Затем над источником я добавил свой массив, чтобы он мог действовать как глобальная переменная, и плагин мог ссылаться на него.

Тогда оставалось просто поменять ссылки.

Я думаю, это позволит вам также добавить перетаскивание, как и снова, если встроенный список доступен только для чтения, то как еще вы могли бы добавить перетаскиваемые файлы в список?

:))

Кэри Абрамофф
источник
4
Я написал слишком рано .... похоже, что в тот момент, когда кто-то устанавливает переменную равной списку файлов, проблема только для чтения возвращается ... Таким образом, я решил сделать два раза и немного болезненно, но эффективно ... Я продолжаю видимый список файлов для загрузки, и отсюда пользователь может удалить ... очевидно, что удаление тега <li> в теге <ul> несложно ... поэтому единственный метод, который я придумал, - это сохранить вторичный список удаленных файлов и обращайтесь к нему во время процесса загрузки ... поэтому, если файл находится в списке загрузки, я просто пропускаю его, и пользователь ничего не понимает.
Кэри Абрамофф
Когда вы назначаете FileListобъект myReadWriteListпеременной, он меняет свой тип с Arrayна FileList, так что это не решение.
adlr0
-2

Я просто меняю тип ввода на текст и обратно в файл: D

maLikiz
источник
Это считается комментарием
Иван Калоянов
Как должен работать? Как ты этого добился?
Ульрих Дохо, 01