如何从文件列表中删除文件


111

我正在使用HTML5构建拖放式Web应用程序,并将文件拖放到div上,当然要获取dataTransfer对象,这给了我FileList

现在,我想删除一些文件,但是我不知道怎么办,或者甚至可能。

最好是我只想从FileList中删除它们;我没有用。但是,如果那不可能,我是否应该在与FileList交互的代码中编写检查代码?那看起来很麻烦。


只是好奇:您为什么要这样做?为什么对用户选择的(某些)文件说“我没有用”?
Marcel Korpel

23
可能更多,以便用户可以在上传之前删除文件。如果您最初选择的是20号,然后又决定实际上不希望上传14号,那么您不能仅仅删除该14号,就必须重新开始(这有点麻烦)。我认为将FileList设置为只读是一个不好的疏忽,除非我没有看到一些安全隐患。
拉斐尔

直接从输入的FileList中删除文件存在安全问题,但您可以在关闭文件上传对话框后立即克隆该FileList,然后修改此克隆并在通过ajax发布时使用它
alex_1948511

Answers:


147

如果只想删除几个选定的文件,则不能。您链接到的File API Working Draft包含注释:

HTMLInputElement接口[HTML5]有一个只读 FileList 属性,[...]
[重点煤矿]

阅读了一些HTML 5工作草案后,我遇到了Common inputelement APIs。您似乎可以通过将对象的属性设置为空字符串来删除整个文件列表,例如:valueinput

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

顺便说一句,使用Web应用程序中的文件一文也可能引起您的兴趣。


1
请注意,属性是只读并不能意味着你不能改变它指向的对象。您可以操纵FileList(如果可能的话),这仅意味着您不能为其分配新的FileList。
罗宾·贝隆

1
@RobinBerjon Chrome似乎忽略了“ readonly”属性,而FireFox不允许进行写操作。不幸的是,您仅建议操作 FileList的建议在FireFox中也不起作用。
borisdiakur 2013年

1
length我认为只有 只读。我尝试删除带有拼接的项目,但在Chrome中失败。
zhiyelee 2015年

有什么办法可以添加吗?
2015年

1
@streetlight 如果站点所有者可以确定要从用户计算机上载哪些文件,那将是一个巨大的安全漏洞。
马塞尔·科佩尔

29

该问题已被标记为已回答,但是我想分享一些信息,这些信息可能对使用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-谢谢,我同意它比较简单,它被列为替代答案。但是,这取决于一个浏览器必须支持以及是否需要使用pollyfill才能使用Array.from()。请参阅:stackoverflow.com/a/36810954/943435
Roberto '18

您实际上如何修改FileList?将此新数组分配给输入fileDialog.files = fileBuffer
eozzy

@ 3zzy-可以修改FileList,但只能在现代浏览器中进行。有关详细信息,请参见此SO问题:stackoverflow.com/a/47522812/943435
Roberto

22

如果您以常绿浏览器(Chrome,Firefox,Edge,但也可以在Safari 9+中运行)为目标,或者您可以负担得起polyfill,则可以使用以下方式将FileList转换为数组Array.from()

let fileArray = Array.from(fileList);

这样就很容易File像处理其他数组一样处理s数组。


完善!您知道IE支持如何吗?或者,也许您可​​以共享一个指向polyfill的链接?
Serhii Matrunchyk

我还没有尝试过,但这是第一个Google结果;)github.com/mathiasbynens/Array.from
adlr0

它只会让您处理fileArray不了fileList
VipinKundal

12

由于我们处于HTML5领域,因此这是我的解决方案。要点是,将文件推送到Array而不是将它们保留在FileList中,然后使用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."));

阿贾克斯是我猜的唯一方法吗?
穆罕默德·乌默尔

10

我已经找到了非常快捷的解决方案。在许多流行的浏览器(Chrome,Firefox,Safari)中经过测试;

首先,您必须将FileList转换为数组

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

删除特定元素使用此

newFileList.splice(index,1);

12
您从创建新的变量event.target.files未链接到输入,因此它不能只是局部变量改变什么..
Maksims Kitajevs

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请求上传文件的,因此我能够通过formdata追加发布FileList克隆的数据。功能是您可以随意拖放或选择多个文件(再次选择文件不会重置克隆的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>

样式。我必须标记其中一些!重要的是要覆盖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。


-1

您可能希望创建一个数组,并使用该数组代替只读文件列表。

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

之后,请针对列表而不是内置列表进行上传。我不确定您正在使用的上下文,但我正在使用一个发现的jquery插件,而我要做的就是获取该插件的源代码,并使用<script>标签。然后在源代码上方添加了我的数组,以便它可以用作全局变量,并且插件可以引用它。

然后,只需换掉参考文献即可。

我认为这将允许您再次添加拖放操作,如果内置列表是只读的,那么如何将拖放的文件放入列表中?

:))


4
我写得太早了...。似乎当设置变量等于文件列表的那一刻,只读问题又出现了...。因此,我选择做的是两折,但有些痛苦但有效...我坚持可见的文件列表,用户可以从此处删除...显然,删除<ul>标记中的<li>标记很简单...所以我想出的唯一方法是保留第二个列表删除的文件并在上载过程中对其进行引用...因此,如果该文件在上载列表中,我将跳过该文件,而用户则不明智。
Cary Abramoff 2011年

当您将FileList对象分配给myReadWriteList变量时,它会将其类型从更改ArrayFileList,因此这不是解决方案。
adlr0

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.