Come rimuovo un file da FileList


111

Sto costruendo un'applicazione web drag-and-drop-to-upload utilizzando HTML5, e sto rilasciando i file su un div e ovviamente sto recuperando l'oggetto dataTransfer, che mi dà FileList .

Ora voglio rimuovere alcuni file, ma non so come o se sia possibile.

Preferibilmente mi piacerebbe semplicemente cancellarli da FileList; Non ne ho bisogno. Ma se ciò non è possibile, dovrei invece scrivere controlli nel codice che interagisce con FileList? Sembra complicato.


Solo curioso: perché vuoi farlo? Perché dici "Non ne ho bisogno" su (alcuni) file selezionati dall'utente?
Marcel Korpel

23
Probabilmente è più così che l'utente possa rimuovere i file prima del caricamento. Se originariamente avevi selezionato 20 e poi decidi che in realtà non vuoi caricare il 14, allora non puoi semplicemente rimuoverlo, devi ricominciare tutto da capo (che è un po 'una seccatura). Penso che rendere FileList in sola lettura sia una cattiva svista, a meno che non ci siano implicazioni sulla sicurezza che non vedo.
Rafael

Sono problemi di sicurezza con l'eliminazione di file direttamente dall'elenco FileList dell'input, ma puoi clonarlo immediatamente dopo aver chiuso la finestra di dialogo di caricamento del file e quindi modificare questo clone e usarlo quando
pubblichi

Risposte:


147

Se desideri eliminare solo alcuni dei file selezionati: non puoi. La bozza di lavoro dell'API file a cui ti sei collegato contiene una nota:

L' HTMLInputElementinterfaccia [HTML5] ha un attributo di sola lettura FileList , […]
[enfasi mia]

Leggendo un po 'della bozza di lavoro HTML 5, mi sono imbattuto nelle API degli elementi comuniinput . Sembra che tu possa eliminare l' intero elenco di file impostando la valueproprietà inputdell'oggetto su una stringa vuota, come:

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

A proposito, potrebbe interessare anche l'articolo Utilizzo di file da applicazioni Web .


1
Nota che un attributo di sola lettura non significa che non puoi cambiare l'oggetto a cui punta. Puoi manipolare FileList (se fosse possibile), significa semplicemente che non puoi assegnargli un nuovo FileList.
Robin Berjon

1
@RobinBerjon Chrome sembra ignorare l'attributo "readonly" mentre FireFox non consente operazioni di scrittura. Sfortunatamente il tuo suggerimento di manipolare solo il FileList non funziona neanche in FireFox.
borisdiakur

1
Solo il lengthè di sola lettura, credo. Provo a eliminare un elemento con giunzione, non riesce in Chrome.
zhiyelee

C'è un modo per aggiungere?
lampione

1
@streetlight Sarebbe un'enorme vulnerabilità di sicurezza, se il proprietario del sito potesse determinare quali file caricare dalla macchina di un utente.
Marcel Korpel

29

Questa domanda è già stata contrassegnata come risposta, ma vorrei condividere alcune informazioni che potrebbero aiutare altri con l'utilizzo di FileList.

Sarebbe conveniente trattare un FileList come un array, ma metodi come sort, shift, pop e slice non funzionano. Come altri hanno suggerito, puoi copiare FileList in un array. Tuttavia, invece di utilizzare un ciclo, esiste una semplice soluzione di una riga per gestire questa conversione.

 // 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;
 });

Testato bene in FF, Chrome e IE10 +


4
Array.from(fileDialog.files)è più semplice
Muhammad Umer

1
@ Muhammad Umer - Grazie, sono d'accordo che è più semplice ed è elencato come risposta alternativa. Tuttavia, dipende da quali browser si devono supportare e se richiedono un pollyfill per utilizzare Array.from (). Vedi: stackoverflow.com/a/36810954/943435
Roberto

Come si modifica effettivamente FileList? Assegnare questo nuovo array all'ingresso fileDialog.files = fileBuffer ?
eozzy

@ 3zzy - È possibile modificare FileList, ma solo nei browser moderni. Vedi queste domande SO per i dettagli: stackoverflow.com/a/47522812/943435
Roberto

22

Se stai prendendo di mira i browser evergreen (Chrome, Firefox, Edge, ma funziona anche in Safari 9+) o puoi permetterti un polyfill, puoi trasformare il FileList in un array usando in Array.from()questo modo:

let fileArray = Array.from(fileList);

Quindi è facile gestire l'array di Files come qualsiasi altro array.


Perfetto! Sai che ne dici del supporto di IE? O forse puoi condividere un collegamento a un polyfill?
Serhii Matrunchyk

Non l'ho provato ma questo è il primo risultato di Google;) github.com/mathiasbynens/Array.from
adlr0

E 'solo consentirà di gestire fileArraynon fileList.
VipinKundal,

12

Dato che siamo nel regno HTML5, questa è la mia soluzione. Il succo è che si inviano i file a un Array invece di lasciarli in un FileList, quindi utilizzando XHR2, si inviano i file a un oggetto FormData. Esempio sotto.

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 è l'unico modo quindi immagino?
Muhammad Umer

10

Ho trovato una soluzione molto rapida e breve per questo. Testato in molti browser popolari (Chrome, Firefox, Safari);

Per prima cosa, devi convertire FileList in un array

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

per eliminare l'elemento in particolare usa questo

newFileList.splice(index,1);

12
Hai creato una nuova variabile da event.target.filescui non è collegata all'input, quindi non può cambiare nulla tranne la tua variabile locale ..
Maksims Kitajevs

6

So che questa è una vecchia domanda, ma è in cima ai motori di ricerca per quanto riguarda questo problema.

le proprietà nell'oggetto FileList non possono essere eliminate ma almeno su Firefox possono essere modificate . La mia soluzione alternativa a questo problema era aggiungere una proprietà IsValid=truea quei file che hanno superato il controllo ea IsValid=falsequelli che non lo hanno superato .

quindi faccio scorrere l'elenco per assicurarmi che solo le proprietà con IsValid=truevengano aggiunte a FormData .


formdata, quindi li invii tramite ajax?
Muhammad Umer

1

Potrebbe esserci un modo più elegante per farlo, ma ecco la mia soluzione. Con Jquery

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

Fondamentalmente si blocca il valore dell'input. Clonalo e metti il ​​clone al posto di quello vecchio.


1

Questo è estemporaneo, ma ho avuto lo stesso problema che ho risolto in questo modo. Nel mio caso stavo caricando i file tramite richiesta XMLHttp, quindi sono stato in grado di pubblicare i dati clonati di FileList tramite l'aggiunta di dati del modulo. La funzionalità è che puoi trascinare e rilasciare o selezionare più file tutte le volte che vuoi (selezionando nuovamente i file non si ripristinerà l'Elenco file clonato), rimuovere qualsiasi file desiderato dall'elenco dei file (clonati) e inviare tramite xmlhttprequest qualunque cosa fosse lasciato lì. Questo è quello che ho fatto. È il mio primo post qui, quindi il codice è un po 'disordinato. Scusate. Ah, e ho dovuto usare jQuery invece di $ come era nello script 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;
    }
};

Ora l'html e gli stili per questo. Sono un principiante, ma tutto questo in realtà ha funzionato per me e mi ci è voluto un po 'per capirlo.

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

Gli stili per quello. Ho dovuto contrassegnarne alcuni! Importanti per sovrascrivere il comportamento di 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;
}

Spero che aiuti.


1

Grazie @Nicholas Anderson semplice e diretto, ecco il tuo codice applicato e lavorando al mio codice usando 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>

CODICE JS

   function cleanInputs(fileEle){
    $(fileEle).val("");
    var parEle = $(fileEle).parent();
    var newEle = $(fileEle).clone()
    $(fileEle).remove();
    $(parEle).prepend(newEle);
}


0

Se hai la fortuna di inviare una richiesta di post al database con i file e hai i file che vuoi inviare nel tuo DOM

puoi semplicemente controllare se il file nell'elenco dei file è presente nel tuo DOM e, naturalmente, se non lo è, semplicemente non inviare quell'elemento a de DB.


-1

Potresti voler creare un array e usarlo al posto dell'elenco di file di sola lettura.

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

Dopo quel punto, esegui il caricamento sulla tua lista invece che sulla lista incorporata. Non sono sicuro del contesto in cui stai lavorando ma sto lavorando con un plugin jquery che ho trovato e quello che dovevo fare era prendere il sorgente del plugin e metterlo nella pagina usando<script> tag. Quindi sopra la sorgente ho aggiunto il mio array in modo che possa agire come una variabile globale e il plugin possa farvi riferimento.

Quindi è stata solo questione di scambiare i riferimenti.

Penso che questo ti consentirebbe di aggiungere di nuovo il drag & drop, se l'elenco integrato è di sola lettura, in quale altro modo potresti inserire i file rilasciati nell'elenco?

:))


4
Ho scritto troppo presto .... sembra che nel momento in cui si imposta una var per eguagliare l'elenco dei file il problema di sola lettura ritorna .... Quindi quello che ho deciso di fare è duplice e leggermente doloroso ma efficace ... tengo un elenco visibile di file per il caricamento e da qui l'utente può rimuovere ... ovviamente rimuovere un tag <li> in un tag <ul> è semplice ... quindi l'unico metodo che ho escogitato è mantenere un elenco secondario di file rimossi e vi faccio riferimento durante il processo di caricamento ... quindi se il file è nell'elenco di caricamento, lo salto e l'utente non è più saggio.
cary abramoff

Quando si assegna l' FileListoggetto alla myReadWriteListvariabile, cambia il suo tipo da Arraya FileList, quindi questa non è una soluzione.
adlr0

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.