Wie entferne ich eine Datei aus der Dateiliste?

110

Ich erstelle eine Webanwendung per Drag & Drop zum Hochladen mit HTML5, lege die Dateien auf einem div ab und rufe natürlich das dataTransfer-Objekt ab, das mir die FileList gibt .

Jetzt möchte ich einige der Dateien entfernen, aber ich weiß nicht wie oder ob es überhaupt möglich ist.

Am liebsten möchte ich sie einfach aus der Dateiliste löschen. Ich habe keine Verwendung für sie. Aber wenn das nicht möglich ist, sollte ich stattdessen Checks in Code schreiben, der mit der FileList interagiert? Das scheint umständlich.

Heilemann
quelle
Nur neugierig: Warum willst du das machen? Warum sagen Sie "Ich habe keine Verwendung für sie" über (einige) Dateien, die der Benutzer ausgewählt hat?
Marcel Korpel
23
Es ist wahrscheinlich eher so, dass der Benutzer Dateien vor dem Hochladen entfernen kann. Wenn Sie ursprünglich 20 ausgewählt haben und dann entscheiden, dass Sie die 14. nicht hochladen möchten, können Sie diese nicht einfach entfernen, sondern müssen von vorne beginnen (was ein bisschen schmerzhaft ist). Ich denke, FileList schreibgeschützt zu machen ist ein schlechtes Versehen, es sei denn, es gibt Sicherheitsaspekte, die ich nicht sehe.
Rafael
Es gibt Sicherheitsprobleme beim direkten Löschen von Dateien aus der Dateiliste der Eingabe. Sie können diese Dateiliste jedoch sofort nach dem Schließen des Dialogfelds zum Hochladen von Dateien klonen und diesen Klon dann ändern und beim Posten über
Ajax verwenden

Antworten:

146

Wenn Sie nur einige der ausgewählten Dateien löschen möchten, können Sie dies nicht. Der von Ihnen verlinkte Datei-API-Arbeitsentwurf enthält einen Hinweis:

Die HTMLInputElementSchnittstelle [HTML5] hat ein schreibgeschütztes FileList Attribut, […]
[Hervorhebung von mir]

Beim Lesen eines Teils des HTML 5-Arbeitsentwurfs stieß ich auf die Common- inputElement-APIs . Anscheinend können Sie die gesamte Dateiliste löschen, indem Sie die valueEigenschaft des inputObjekts auf eine leere Zeichenfolge setzen, z.

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

Übrigens könnte auch der Artikel Verwenden von Dateien aus Webanwendungen von Interesse sein.

Marcel Korpel
quelle
1
Beachten Sie, dass ein schreibgeschütztes Attribut nicht bedeutet, dass Sie das Objekt, auf das es zeigt, nicht ändern können. Sie können die Dateiliste bearbeiten (falls dies möglich wäre). Dies bedeutet lediglich, dass Sie ihr keine neue Dateiliste zuweisen können.
Robin Berjon
1
@RobinBerjon Chrome scheint das Attribut "nur lesen" zu ignorieren, während FireFox keine Schreibvorgänge zulässt. Leider Ihr Vorschlag einfach zu manipulieren die Dateiliste nicht funktioniert in FireFox auch nicht .
Borisdiakur
1
Nur das lengthist schreibgeschützt, denke ich. Ich versuche ein Element mit Spleiß zu löschen, es schlägt in Chrome fehl.
Zhiyelee
Gibt es eine Möglichkeit zum Hinzufügen?
Straßenlaterne
1
@streetlight Das wäre eine große Sicherheitslücke, wenn der Websitebesitzer bestimmen kann, welche Dateien von einem Computer eines Benutzers hochgeladen werden sollen.
Marcel Korpel
29

Diese Frage wurde bereits als beantwortet markiert, aber ich möchte einige Informationen weitergeben, die anderen bei der Verwendung von FileList helfen könnten.

Es wäre praktisch, eine Dateiliste als Array zu behandeln, aber Methoden wie Sortieren, Verschieben, Pop und Slice funktionieren nicht. Wie andere vorgeschlagen haben, können Sie die Dateiliste in ein Array kopieren. Anstatt eine Schleife zu verwenden, gibt es jedoch eine einfache einzeilige Lösung für diese Konvertierung.

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

In FF, Chrome und IE10 + OK getestet

Roberto
quelle
4
Array.from(fileDialog.files)ist einfacher
Muhammad Umer
1
@ Muhammad Umer - Danke, ich stimme zu, dass es einfacher ist und als alternative Antwort aufgeführt ist. Es hängt jedoch davon ab, welche Browser unterstützt werden müssen und ob für die Verwendung von Array.from () eine Pollyfill erforderlich ist. Siehe: stackoverflow.com/a/36810954/943435
Roberto
Wie können Sie die Dateiliste tatsächlich ändern? Dieses neue Array dem Eingang zuweisen fileDialog.files = fileBuffer ?
eozzy
@ 3zzy - Es ist möglich, FileList zu ändern, jedoch nur in modernen Browsern. Siehe diese SO Fragen für Details: stackoverflow.com/a/47522812/943435
Roberto
22

Wenn Sie auf immergrüne Browser abzielen (Chrome, Firefox, Edge, funktioniert aber auch in Safari 9+) oder sich eine Polyfüllung leisten können, können Sie die Dateiliste Array.from()wie folgt in ein Array verwandeln :

let fileArray = Array.from(fileList);

Dann ist es einfach, das Array von Files wie jedes andere Array zu handhaben .

adlr0
quelle
Perfekt! Wissen Sie, wie man IE unterstützt? Oder können Sie einen Link zu einer Polyfüllung teilen?
Serhii Matrunchyk
Ich habe es nicht ausprobiert, aber dies ist das erste Google-Ergebnis;) github.com/mathiasbynens/Array.from
adlr0
Es wird Ihnen nur erlauben, fileArraynicht damit umzugehen fileList.
VipinKundal
12

Da wir uns im HTML5-Bereich befinden, ist dies meine Lösung. Das Wesentliche ist, dass Sie die Dateien in ein Array verschieben, anstatt sie in einer Dateiliste zu belassen, und dann mit XHR2 die Dateien in ein FormData-Objekt verschieben. Beispiel unten.

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."));
Joshua W.
quelle
Ajax ist der einzige Weg, denke ich?
Muhammad Umer
10

Ich habe dafür eine sehr schnelle und kurze Problemumgehung gefunden. Getestet in vielen gängigen Browsern (Chrome, Firefox, Safari);

Zuerst müssen Sie FileList in ein Array konvertieren

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

Verwenden Sie dies, um das jeweilige Element zu löschen

newFileList.splice(index,1);
MeVimalkumar
quelle
12
Sie haben eine neue Variable erstellt, event.target.filesdie nicht mit der Eingabe verknüpft ist, sodass sie nichts außer Ihrer lokalen Variablen ändern kann.
Maksims Kitajevs
6

Ich weiß, dass dies eine alte Frage ist, aber sie hat in Bezug auf dieses Problem einen hohen Stellenwert bei Suchmaschinen.

Eigenschaften im FileList- Objekt können nicht gelöscht werden, aber zumindest in Firefox können sie geändert werden . Meine Problemumgehung bestand darin IsValid=true, den Dateien, die die Prüfung und bestanden haben , eine Eigenschaft hinzuzufügenIsValid=false denen, die dies nicht getan haben .

Dann durchlaufe ich einfach die Liste, um sicherzustellen, dass nur die Eigenschaften mit IsValid=truezu FormData hinzugefügt werden .

A. Richards
quelle
Formdaten, also senden Sie sie über Ajax?
Muhammad Umer
1

Es gibt vielleicht einen eleganteren Weg, dies zu tun, aber hier ist meine Lösung. Mit Jquery

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

Grundsätzlich klemmen Sie den Wert der Eingabe. Klonen Sie es und setzen Sie den Klon anstelle des alten ein.

Nicholas Anderson
quelle
1

Dies ist vorübergehend, aber ich hatte das gleiche Problem, das ich auf diese Weise gelöst habe. In meinem Fall habe ich die Dateien über eine XMLHttp-Anforderung hochgeladen, sodass ich die geklonten FileList-Daten über angehängte Formulardaten veröffentlichen konnte. Die Funktionalität besteht darin, dass Sie mehrere Dateien so oft ziehen und ablegen oder auswählen können, wie Sie möchten (durch erneutes Auswählen von Dateien wird die geklonte Dateiliste nicht zurückgesetzt), jede gewünschte Datei aus der (geklonten) Dateiliste entfernt und über xmlhttprequest gesendet, was auch immer war dort gelassen. Das habe ich getan. Es ist mein erster Beitrag hier, daher ist der Code etwas chaotisch. Es tut uns leid. Ah, und ich musste jQuery anstelle von $ verwenden, wie es im Joomla-Skript war.

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

Nun das HTML und die Stile dafür. Ich bin ein ziemlicher Neuling, aber das alles hat tatsächlich für mich funktioniert und ich habe eine Weile gebraucht, um es herauszufinden.

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

Die Stile dafür. Ich musste einige von ihnen markieren! Wichtig, um das Verhalten von Joomla außer Kraft zu setzen.

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

Ich hoffe das hilft.

Eric
quelle
1

Danke @Nicholas Anderson einfach und direkt, hier ist Ihr Code angewendet und arbeitet mit jquery an meinem Code.

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 CODE

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

In vue js:

self.$refs.inputFile.value = ''

Pragati Dugar
quelle
0

Wenn Sie das Glück haben, eine Post-Anfrage mit den Dateien an die Datenbank zu senden, und Sie die Dateien haben, die Sie in Ihrem DOM senden möchten

Sie können einfach überprüfen, ob die Datei in der Dateiliste in Ihrem DOM vorhanden ist, und wenn dies nicht der Fall ist, senden Sie dieses Element natürlich nicht an de DB.

Neku80
quelle
-1

Möglicherweise möchten Sie ein Array erstellen und dieses anstelle der schreibgeschützten Dateiliste verwenden.

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

Führen Sie nach diesem Zeitpunkt das Hochladen anhand Ihrer Liste anstelle der integrierten Liste durch. Ich bin mir nicht sicher, in welchem ​​Kontext Sie arbeiten, aber ich arbeite mit einem JQuery-Plugin, das ich gefunden habe, und ich musste die Quelle des Plugins nehmen und es mit auf die Seite setzen<script> Tags . Dann habe ich über der Quelle mein Array hinzugefügt, damit es als globale Variable fungieren kann und das Plugin darauf verweisen kann.

Dann ging es nur noch darum, die Referenzen auszutauschen.

Ich denke, dies würde es Ihnen ermöglichen, Drag & Drop auch wieder hinzuzufügen. Wenn die integrierte Liste schreibgeschützt ist, wie könnten Sie dann die abgelegten Dateien in die Liste aufnehmen?

:))

Cary Abramoff
quelle
4
Ich habe zu früh geschrieben ... es scheint, dass in dem Moment, in dem man eine Variable so einstellt, dass sie der Dateiliste entspricht, die schreibgeschützte Ausgabe zurückkommt ... Ich habe mich also für zwei Dinge entschieden und bin leicht schmerzhaft, aber effektiv ... ich behalte Eine sichtbare Liste von Dateien zum Hochladen und von hier aus kann der Benutzer sie entfernen. Offensichtlich ist das Entfernen eines <li> -Tags in einem <ul>-Tag unkompliziert. Die einzige Methode, die ich mir ausgedacht habe, besteht darin, eine sekundäre Liste zu führen von entfernten Dateien und verweisen während des Upload-Vorgangs darauf. Wenn sich die Datei in der Upload-Liste befindet, überspringe ich sie einfach und der Benutzer ist nicht klüger.
Cary Abramoff
Wenn Sie die zuweisen FileList Objekt der myReadWriteListVariablen , ändert es seinen Typ von Arraynach FileList, sodass dies keine Lösung ist.
adlr0
-2

Ich ändere einfach die Art der Eingabe in den Text und zurück in die Datei: D.

maLikiz
quelle
Dies wird als Kommentar angesehen
Ivan Kaloyanov
Wie soll das funktionieren? Wie haben Sie das erreicht?
Ulrich Dohou