Bagaimana cara menghapus file dari FileList

111

Saya membangun aplikasi web seret-dan-lepas-untuk-unggah menggunakan HTML5, dan saya meletakkan file ke div dan tentu saja mengambil objek dataTransfer, yang memberi saya FileList .

Sekarang saya ingin menghapus beberapa file, tetapi saya tidak tahu caranya, atau bahkan mungkin.

Lebih disukai saya hanya ingin menghapusnya dari FileList; Aku tidak berguna untuk mereka. Tetapi jika itu tidak memungkinkan, haruskah saya menulis cek dalam kode yang berinteraksi dengan FileList? Sepertinya itu tidak praktis.

Heilemann
sumber
Hanya penasaran: mengapa Anda ingin melakukan ini? Mengapa Anda mengatakan "Saya tidak berguna untuk mereka" tentang (beberapa) file yang dipilih pengguna?
Marcel Korpel
23
Mungkin lebih dari itu pengguna dapat menghapus file sebelum mengunggah. Jika Anda awalnya memilih 20 dan kemudian Anda memutuskan bahwa Anda sebenarnya tidak ingin mengunggah tanggal 14, maka Anda tidak bisa begitu saja menghapus yang itu, Anda harus memulai dari awal lagi (yang agak merepotkan). Saya pikir membuat FileList hanya bisa dibaca adalah pengawasan yang buruk, kecuali ada implikasi keamanan yang tidak saya lihat.
Rafael
Ini masalah keamanan dengan menghapus file dari FileList input secara langsung tetapi Anda dapat mengkloning FileList itu segera setelah menutup dialog unggah file dan kemudian memodifikasi klon ini & menggunakannya saat memposting melalui ajax
alex_1948511

Jawaban:

147

Jika Anda ingin menghapus hanya beberapa dari file yang dipilih: Anda tidak bisa. The Berkas API Kerja Draft Anda terhubung ke berisi catatan:

The HTMLInputElementantarmuka [HTML5] memiliki readonly FileList atribut, [...]
[penekanan]

Membaca sedikit Konsep Kerja HTML 5, saya menemukan API elemen Umuminput . Tampaknya Anda dapat menghapus seluruh daftar file dengan mengatur valueproperti inputobjek menjadi string kosong, seperti:

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

BTW, artikel Menggunakan file dari aplikasi web mungkin juga menarik.

Marcel Korpel
sumber
1
Perhatikan bahwa atribut menjadi hanya-baca tidak berarti bahwa Anda tidak dapat mengubah objek yang ditunjuknya. Anda dapat memanipulasi FileList (jika itu mungkin), itu berarti Anda tidak dapat menetapkan FileList baru untuk itu.
Robin Berjon
1
@RobinBerjon Chrome tampaknya mengabaikan atribut ´readonly´ sementara FireFox tidak mengizinkan operasi tulis. Sayangnya saran Anda untuk hanya memanipulasi FileList juga tidak berfungsi di FireFox.
borisdiakur
1
Hanya yang lengthdibaca saja, saya pikir. Saya mencoba menghapus item dengan sambungan, gagal di Chrome.
zhiyelee
Apakah ada cara untuk menambahkan?
lampu jalan
1
@streetlight Itu akan menjadi kerentanan keamanan yang sangat besar , jika pemilik situs dapat menentukan file mana yang akan diunggah dari komputer pengguna.
Marcel Korpel
29

Pertanyaan ini telah ditandai terjawab, tetapi saya ingin membagikan beberapa informasi yang mungkin dapat membantu orang lain dalam menggunakan FileList.

Akan lebih mudah untuk memperlakukan FileList sebagai sebuah array, tetapi metode seperti sort, shift, pop, dan slice tidak berfungsi. Seperti yang disarankan orang lain, Anda dapat menyalin FileList ke array. Namun, daripada menggunakan loop, ada solusi satu baris sederhana untuk menangani konversi ini.

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

Diuji OK di FF, Chrome, dan IE10 +

Roberto
sumber
4
Array.from(fileDialog.files)lebih sederhana
Muhammad Umer
1
@Muhammad Umer - Terima kasih, saya setuju bahwa ini lebih sederhana dan terdaftar sebagai jawaban alternatif. Namun, itu tergantung pada browser mana yang harus didukung dan apakah mereka memerlukan pollyfill untuk menggunakan Array.from (). Lihat: stackoverflow.com/a/36810954/943435
Roberto
Bagaimana Anda sebenarnya memodifikasi FileList? Tetapkan array baru ini ke input fileDialog.files = fileBuffer ?
eozzy
@ 3zzy - FileList dapat dimodifikasi, tetapi hanya di browser modern. Lihat pertanyaan SO ini untuk detailnya: stackoverflow.com/a/47522812/943435
Roberto
22

Jika Anda menargetkan browser evergreen (Chrome, Firefox, Edge, tetapi juga berfungsi di Safari 9+) atau Anda dapat membeli polyfill, Anda dapat mengubah FileList menjadi array dengan menggunakan Array.from()seperti ini:

let fileArray = Array.from(fileList);

Maka mudah untuk menangani larik Fileseperti larik lainnya.

adlr0
sumber
Sempurna! Apakah Anda tahu bagaimana dengan dukungan IE? Atau mungkin Anda dapat membagikan tautan ke polyfill?
Serhii Matrunchyk
Saya belum mencobanya tetapi ini adalah hasil google pertama;) github.com/mathiasbynens/Array.from
adlr0
Ini hanya akan memungkinkan Anda untuk fileArraytidak menangani fileList.
VipinKundal
12

Karena kita berada di ranah HTML5, inilah solusi saya. Intinya adalah Anda mendorong file ke Array alih-alih meninggalkannya di FileList, lalu menggunakan XHR2, Anda mendorong file ke objek FormData. Contoh di bawah ini.

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.
sumber
ajax adalah satu-satunya cara, ya?
Muhammad Umer
10

Saya telah menemukan solusi yang sangat cepat & singkat untuk ini. Diuji di banyak browser populer (Chrome, Firefox, Safari);

Pertama, Anda harus mengonversi FileList menjadi Array

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

untuk menghapus elemen tertentu gunakan ini

newFileList.splice(index,1);
MeVimalkumar
sumber
12
Anda membuat variabel baru event.target.filesyang tidak ditautkan ke masukan sehingga tidak dapat mengubah apa pun kecuali variabel lokal Anda ..
Maksims Kitajevs
6

Saya tahu ini adalah pertanyaan lama, tetapi peringkatnya tinggi di mesin telusur terkait dengan masalah ini.

properti di objek FileList tidak dapat dihapus tetapi setidaknya di Firefox, properti dapat diubah . Solusi saya untuk masalah ini adalah menambahkan properti IsValid=trueke file yang lolos pemeriksaan dan IsValid=falseyang tidak.

kemudian saya hanya mengulang daftar untuk memastikan bahwa hanya properti dengan IsValid=trueyang ditambahkan ke FormData .

A. Richards
sumber
formdata, jadi Anda mengirimnya melalui ajax?
Muhammad Umer
1

Mungkin ada cara yang lebih elegan untuk melakukan ini, tetapi inilah solusi saya. Dengan Jquery

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

Pada dasarnya Anda membersihkan nilai input. Klon dan letakkan klon di tempat yang lama.

Nicholas Anderson
sumber
1

Ini bersifat sementara, tetapi saya memiliki masalah yang sama yang saya selesaikan dengan cara ini. Dalam kasus saya, saya mengunggah file melalui permintaan XMLHttp, jadi saya dapat memposting data kloning FileList melalui penambahan formdata. Fungsinya adalah Anda dapat menarik dan melepas atau memilih beberapa file sebanyak yang Anda inginkan (memilih file lagi tidak akan mereset FileList kloning), menghapus file apa pun yang Anda inginkan dari daftar file (kloning), dan mengirimkan melalui xmlhttprequest apa pun yang tertinggal disana. Inilah yang saya lakukan. Ini adalah posting pertama saya di sini jadi kodenya sedikit berantakan. Maaf. Ah, dan saya harus menggunakan jQuery daripada $ seperti yang ada di skrip 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;
    }
};

Sekarang html dan gaya untuk ini. Saya cukup pemula tetapi semua ini benar-benar berhasil untuk saya dan membutuhkan waktu beberapa saat untuk mengetahuinya.

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

Gaya untuk itu. Saya harus menandai beberapa di antaranya! Penting untuk mengesampingkan perilaku 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;
}

Saya harap ini membantu.

Eric
sumber
1

Terima kasih @Nicholas Anderson sederhana dan lurus, berikut kode Anda diterapkan dan bekerja di kode saya menggunakan 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 CODE

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

In vue js:

self.$refs.inputFile.value = ''

Pragati Dugar
sumber
0

Jika Anda beruntung mengirim permintaan posting ke database dengan file dan Anda memiliki file yang ingin Anda kirim di DOM Anda

Anda cukup memeriksa apakah file dalam daftar file ada di DOM Anda, dan tentu saja jika tidak Anda tidak mengirim elemen itu ke de DB.

Neku80
sumber
-1

Anda mungkin ingin membuat sebuah array dan menggunakannya sebagai ganti dari daftar file read-only.

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

Setelah itu lakukan pengunggahan Anda terhadap daftar Anda, bukan daftar bawaan. Saya tidak yakin dengan konteks yang Anda kerjakan tetapi saya bekerja dengan plugin jquery yang saya temukan dan yang harus saya lakukan adalah mengambil sumber plugin dan meletakkannya di halaman menggunakan <script>tag. Kemudian di atas sumber saya menambahkan array saya sehingga dapat bertindak sebagai variabel global dan plugin dapat mereferensikannya.

Maka itu hanya masalah menukar referensi.

Saya pikir ini akan memungkinkan Anda untuk juga menambahkan drag & drop seperti lagi, jika daftar bawaan hanya-baca lalu bagaimana lagi Anda bisa mendapatkan file yang dijatuhkan ke dalam daftar?

:))

cary abramoff
sumber
4
Saya menulis terlalu cepat .... tampaknya saat seseorang menetapkan var untuk menyamai daftar file, masalah hanya-baca muncul kembali .... Jadi apa yang telah saya pilih untuk dilakukan adalah dua kali lipat & sedikit menyakitkan tetapi efektif ... Saya terus daftar file yang terlihat untuk diunggah dan dari sini pengguna dapat menghapus ... jelas menghapus tag <li> dalam tag <ul> sangatlah mudah ... jadi satu-satunya metode yang saya temukan adalah dengan menyimpan daftar sekunder dari file yang dihapus dan merujuknya selama proses upload ... oleh karena itu jika file tersebut ada dalam daftar upload saya lewati saja dan pengguna tidak ada yang lebih bijak.
cary abramoff
Saat Anda menetapkan FileListobjek ke myReadWriteListvariabel, jenisnya berubah dari Arraymenjadi FileList, jadi ini bukan solusi.
adlr0
-2

Saya hanya mengubah jenis input ke teks dan kembali ke file: D

maLikiz
sumber
Ini dianggap sebagai komentar
Ivan Kaloyanov
Bagaimana cara kerjanya? Bagaimana Anda mencapai itu?
Ulrich Dohou