Як видалити файл із списку файлів


111

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

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

Переважно, я хотів би просто видалити їх із списку файлів; Я не маю для них ніякої користі. Але якщо це неможливо, я повинен замість цього записувати чеки в код, який взаємодіє з FileList? Це здається громіздким.


Просто цікаво: чому ти хочеш це робити? Чому ви кажете "я не маю для них ніякої користі" про (деякі) файли, які вибрав користувач?
Марсель Корпель

23
Це, мабуть, більше, щоб користувач міг видаляти файли перед завантаженням. Якщо ви спочатку вибрали 20, а потім вирішили, що насправді не хочете завантажувати 14-ту, тоді ви не можете просто видалити її, вам доведеться починати все заново (що трохи болить). Я думаю, що перегляд файлового списку FileList - це поганий контроль, якщо тільки я не бачу якихось наслідків для безпеки.
Рафаель

Проблеми із безпекою при видаленні файлів із FileList вхідного файлу безпосередньо, але ви можете клонувати цей FileList відразу після закриття діалогового вікна завантаження файлів, а потім змінити цей клон та використовувати його під час публікації через ajax
alex_1948511

Відповіді:


147

Якщо ви хочете видалити лише кілька вибраних файлів, ви не можете. API Файл робочого проекту ви пов'язані з містить примітка:

В HTMLInputElementінтерфейсі [HTML5] є атрибут readonly FileList , […]
[мій наголос]

Прочитавши трохи робочого проекту HTML 5, я натрапив на API загальних inputелементів . Здається, ви можете видалити весь список файлів, встановивши valueвластивість inputоб'єкта на порожній рядок, наприклад:

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

BTW, стаття Використання файлів із веб-додатків також може представляти інтерес.


1
Зверніть увагу, що атрибут, який читається тільки не означає, що ви не можете змінити об'єкт, на який він вказує. Ви можете маніпулювати FileList (якщо це можливо), це просто означає, що ви не можете призначити йому новий FileList.
Робін Берджон

1
@RobinBerjon Chrome, здається, ігнорує атрибут "readonly", тоді як FireFox не дозволяє операції запису. На жаль, ваша пропозиція просто маніпулювати FileList також не працює у FireFox.
borisdiakur

1
lengthДумаю, лише те , що читається тільки. Я намагаюся видалити елемент зі сплайсом, він не вдається в Chrome.
zhiyelee

Чи є якийсь спосіб додати?
ліхтарик

1
@streetlight Це буде величезна вразливість безпеки, якщо власник сайту може визначити, які файли потрібно завантажити з машини користувача.
Марсель Корпель

29

На це запитання вже позначено відповіді, але я хотів би поділитися деякою інформацією, яка може допомогти іншим у використанні FileList.

Було б зручно трактувати FileList як масив, але такі методи, як сортування, зсув, поп і фрагмент, не працюють. Як запропонували інші, ви можете скопіювати 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 - Дякую, я згоден, що це простіше, і це вказано як альтернативну відповідь. Тим не менш, це залежить від того, які браузери потрібно підтримувати, і якщо їм потрібен pollyfill для використання Array.from (). Дивіться: stackoverflow.com/a/36810954/943435
Роберто

Як ви фактично змінюєте FileList? Призначити цей новий масив до входу fileDialog.files = fileBuffer ?
eozzy

@ 3zzy - Можна змінити FileList, але лише в сучасних браузерах. Див це так , питання для деталей: stackoverflow.com/a/47522812/943435
Roberto

22

Якщо ви орієнтовані на вічнозелені веб-переглядачі (Chrome, Firefox, Edge, але також працює в Safari 9+) або ви можете дозволити собі поліфау, ви можете перетворити FileList у масив, використовуючи Array.from()такий:

let fileArray = Array.from(fileList);

Тоді легко обробляти масив Files, як і будь-який інший масив.


Ідеально! Чи знаєте ви про підтримку 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);

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 через додані формуляри. Функціональність полягає в тому, що ви можете перетягувати або випускати або вибирати кілька файлів стільки разів, скільки вам потрібно (вибір файлів знову не скине клонований список файлів), видалити будь-який файл із списку (клонованих) файлів і подати через 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>

Стилі для цього. Мені довелося відзначити деякі з них, важливі для того, щоб перекрити поведінку Джомули.

.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.


-1

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

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

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

Тоді це було лише питання заміни посилань.

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

:))


4
Я писав занадто рано ...., здається, що в момент встановлення вар дорівнювати списку файлів, проблема з readonly повертається .... Таким чином, те, що я вибрав, це зробити в два рази і трохи болісно, ​​але ефективно ... Я продовжую видимий список файлів для завантаження і звідси користувач може видалити ... очевидно, видалення тегу <li> з тегу <ul> є простим ... тому єдиний спосіб, який я придумав, - це збереження вторинного списку видалених файлів і посилайтеся на них під час завантаження ... тому, якщо файл знаходиться у списку завантажень, я просто пропускаю його, а користувач не мудріший.
cary abramoff

Коли ви присвоюєте FileListоб'єкт myReadWriteListзмінній, він змінює його тип з Arrayна FileList, тому це не є рішенням.
adlr0

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.