Làm cách nào để xóa tệp khỏi FileList


111

Tôi đang xây dựng một ứng dụng web kéo và thả để tải lên bằng HTML5 và tôi đang thả các tệp vào một div và tất nhiên là tìm nạp đối tượng dataTransfer, đối tượng này cung cấp cho tôi FileList .

Bây giờ tôi muốn xóa một số tệp, nhưng tôi không biết làm thế nào hoặc thậm chí có thể không.

Tốt nhất là tôi chỉ muốn xóa chúng khỏi FileList; Tôi không có ích cho chúng. Nhưng nếu điều đó là không thể, thay vào đó tôi có nên viết kiểm tra trong mã tương tác với FileList không? Điều đó có vẻ rườm rà.


Chỉ tò mò: tại sao bạn muốn làm điều này? Tại sao bạn lại nói “Tôi không sử dụng được chúng” về (một số) tệp mà người dùng đã chọn?
Marcel Korpel

23
Nó có lẽ nhiều hơn để người dùng có thể xóa các tệp trước khi tải lên. Nếu ban đầu bạn đã chọn 20 và sau đó bạn quyết định thực sự không muốn tải lên thứ 14, thì bạn không thể chỉ xóa cái đó mà phải bắt đầu lại từ đầu (hơi phiền phức). Tôi nghĩ rằng làm cho FileList chỉ đọc là một sự giám sát tồi, trừ khi có một số ngụ ý bảo mật mà tôi không thấy.
Rafael

Đó là rắc rối an ninh với xóa các tập tin từ FileList đầu vào của trực tiếp nhưng bạn có thể sao chép rằng FileList ngay lập tức sau khi đóng hộp thoại tập tin tải lên và sau đó sửa đổi bản sao này và sử dụng nó khi đăng qua ajax
alex_1948511

Câu trả lời:


147

Nếu bạn chỉ muốn xóa một số tệp đã chọn: bạn không thể. Các tập tin API Working Draft bạn liên quan đến chứa một lưu ý:

Các HTMLInputElementgiao diện [HTML5] có một readonly FileList thuộc tính, [...]
[tôi nhấn mạnh]

Đọc một chút về Bản nháp làm việc HTML 5, tôi đã xem qua các API phần tử chunginput . Có vẻ như bạn có thể xóa toàn bộ danh sách tệp bằng cách đặt thuộc valuetính của inputđối tượng thành một chuỗi trống, như:

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

BTW, bài viết Sử dụng tệp từ ứng dụng web cũng có thể được quan tâm.


1
Lưu ý rằng một thuộc tính chỉ đọc không có nghĩa là bạn không thể thay đổi đối tượng mà nó trỏ tới. Bạn có thể thao tác FileList (nếu có thể), điều đó chỉ có nghĩa là bạn không thể gán một FileList mới cho nó.
Robin Berjon

1
@RobinBerjon Chrome dường như bỏ qua thuộc tính ´readonly´ trong khi FireFox không cho phép thao tác ghi. Rất tiếc, đề xuất chỉ thao tác FileList của bạn cũng không hoạt động trong FireFox.
borisdiakur

1
lengthTôi nghĩ chỉ có cái là chỉ đọc. Tôi cố gắng xóa một mục có mối nối, nó không thành công trong Chrome.
zhiyelee

Có cách nào để thêm không?
đèn đường

1
@streetlight Đó sẽ là một lỗ hổng bảo mật lớn , nếu chủ sở hữu trang web có thể xác định tệp nào sẽ tải lên từ máy của người dùng.
Marcel Korpel

29

Câu hỏi này đã được đánh dấu là đã trả lời, nhưng tôi muốn chia sẻ một số thông tin có thể giúp những người khác sử dụng FileList.

Sẽ rất thuận tiện nếu coi FileList như một mảng, nhưng các phương thức như sắp xếp, dịch chuyển, bật và lát không hoạt động. Như những người khác đã đề xuất, bạn có thể sao chép FileList vào một mảng. Tuy nhiên, thay vì sử dụng vòng lặp, có một giải pháp đơn giản một dòng để xử lý chuyển đổi này.

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

Đã kiểm tra OK trong FF, Chrome và IE10 +


4
Array.from(fileDialog.files)đơn giản hơn
Muhammad Umer

1
@Muhammad Umer - Cảm ơn, tôi đồng ý rằng nó đơn giản hơn và nó được liệt kê như một câu trả lời thay thế. Tuy nhiên, nó phụ thuộc vào trình duyệt nào mà người ta phải hỗ trợ và nếu chúng yêu cầu một pollyfill để sử dụng Array.from (). Xem: stackoverflow.com/a/36810954/943435
Roberto

Làm thế nào để bạn thực sự sửa đổi FileList? Gán mảng mới này cho đầu vào fileDialog.files = fileBuffer ?
eozzy

@ 3zzy - Có thể sửa đổi FileList, nhưng chỉ trong các trình duyệt hiện đại. Xem câu hỏi SO này để biết chi tiết: stackoverflow.com/a/47522812/943435
Roberto

22

Nếu bạn đang nhắm mục tiêu các trình duyệt thường xanh (Chrome, Firefox, Edge, nhưng cũng hoạt động trong Safari 9+) hoặc bạn có đủ khả năng mua polyfill, bạn có thể biến FileList thành một mảng bằng cách sử dụng Array.from()như sau:

let fileArray = Array.from(fileList);

Sau đó, thật dễ dàng để xử lý mảng Files giống như bất kỳ mảng nào khác.


Hoàn hảo! Bạn có biết làm thế nào về hỗ trợ IE? Hoặc có thể bạn có thể chia sẻ một liên kết đến một polyfill?
Serhii Matrunchyk

Tôi chưa thử nhưng đây là kết quả đầu tiên trên google;) github.com/mathiasbynens/Array.from
adlr0

Nó sẽ chỉ cho phép bạn xử lý fileArraykhông fileList.
VipinKundal

12

Vì chúng tôi đang ở trong lĩnh vực HTML5, đây là giải pháp của tôi. Ý chính là bạn đẩy tệp vào Mảng thay vì để chúng trong Danh sách tệp, sau đó sử dụng XHR2, bạn đẩy tệp vào đối tượng FormData. Ví dụ bên dưới.

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à cách duy nhất sau đó tôi đoán?
Muhammad Umer

10

Tôi đã tìm ra cách giải quyết rất nhanh và ngắn cho việc này. Đã thử nghiệm trên nhiều trình duyệt phổ biến (Chrome, Firefox, Safari);

Trước tiên, bạn phải chuyển đổi FileList thành Array

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

để xóa phần tử cụ thể, hãy sử dụng cái này

newFileList.splice(index,1);

12
Bạn đã tạo biến mới event.target.filesmà từ đó không được liên kết với đầu vào nên nó không thể thay đổi bất kỳ điều gì ngoại trừ biến cục bộ của bạn ..
Maksims Kitajevs

6

Tôi biết đây là một câu hỏi cũ nhưng nó xếp hạng cao trên các công cụ tìm kiếm liên quan đến vấn đề này.

các thuộc tính trong đối tượng FileList không thể bị xóa nhưng ít nhất trên Firefox, chúng có thể được thay đổi . Giải pháp của tôi cho vấn đề này là thêm một thuộc tính IsValid=truevào những tệp đã vượt qua kiểm tra và những tệp chưa vượt qua IsValid=false.

thì tôi chỉ cần lặp qua danh sách để đảm bảo rằng chỉ các thuộc tính có IsValid=trueđược thêm vào FormData .


formdata, vì vậy bạn gửi chúng qua ajax?
Muhammad Umer

1

Có thể có một cách thanh lịch hơn để làm điều này nhưng đây là giải pháp của tôi. Với Jquery

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

Về cơ bản, bạn xóa giá trị của đầu vào. Sao chép nó và đặt bản sao vào vị trí của cái cũ.


1

Điều này là tạm thời, nhưng tôi đã gặp cùng một vấn đề mà tôi đã giải quyết theo cách này. Trong trường hợp của tôi, tôi đang tải lên các tệp qua yêu cầu XMLHttp, vì vậy tôi có thể đăng dữ liệu nhân bản của FileList thông qua việc nối thêm dữ liệu biểu mẫu. Chức năng là bạn có thể kéo và thả hoặc chọn nhiều tệp bao nhiêu lần tùy thích (chọn lại tệp sẽ không đặt lại FileList đã nhân bản), xóa bất kỳ tệp nào bạn muốn khỏi danh sách tệp (đã nhân bản) và gửi qua xmlhttprequest bất kỳ thứ gì trái có. Đây là những gì tôi đã làm. Đây là bài viết đầu tiên của tôi ở đây nên code hơi lộn xộn. Lấy làm tiếc. Ah, và tôi đã phải sử dụng jQuery thay vì $ như trong Joomla script.

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

Bây giờ là html và các kiểu cho cái này. Tôi là một người mới nhưng tất cả điều này thực sự hiệu quả với tôi và tôi đã mất một thời gian để tìm ra nó.

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

Các phong cách cho điều đó. Tôi đã phải đánh dấu một số trong số chúng! Điều quan trọng để ghi đè hành vi 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;
}

Tôi hi vọng cái này giúp được.


1

Cảm ơn @Nicholas Anderson, đơn giản và dễ hiểu, đây là mã của bạn được áp dụng và hoạt động với mã của tôi bằng cách sử dụng 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>

MÃ JS

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


0

Nếu bạn có may mắn được gửi một yêu cầu đăng bài đến cơ sở dữ liệu với các tệp và bạn có các tệp bạn muốn gửi trong DOM của mình

bạn có thể chỉ cần kiểm tra xem tệp trong danh sách tệp có trong DOM của bạn hay không và tất nhiên nếu không, bạn chỉ cần không gửi phần tử đó đến de DB.


-1

Bạn có thể muốn tạo một mảng và sử dụng mảng đó thay vì danh sách lọc chỉ đọc.

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

Sau thời điểm đó, hãy tải lên dựa trên danh sách của bạn thay vì danh sách tích hợp sẵn. Tôi không chắc về ngữ cảnh bạn đang làm việc nhưng tôi đang làm việc với một plugin jquery mà tôi đã tìm thấy và những gì tôi phải làm là lấy nguồn của plugin và đưa nó vào trang bằng cách sử dụng <script>các thẻ. Sau đó, phía trên nguồn, tôi đã thêm mảng của mình để nó có thể hoạt động như một biến toàn cục và plugin có thể tham chiếu đến nó.

Sau đó, nó chỉ là một vấn đề trao đổi các tài liệu tham khảo.

Tôi nghĩ rằng điều này sẽ cho phép bạn cũng thêm kéo và thả như một lần nữa, nếu danh sách tích hợp ở chế độ chỉ đọc thì làm cách nào khác bạn có thể đưa các tệp đã bỏ vào danh sách?

:))


4
Tôi đã viết quá sớm .... có vẻ như thời điểm người ta đặt một var để bằng với danh sách lọc, vấn đề chỉ đọc xuất hiện trở lại .... Vì vậy, những gì tôi đã chọn làm là hai lần và hơi đau nhưng hiệu quả ... Tôi tiếp tục một danh sách hiển thị các tệp để tải lên và từ đây người dùng có thể xóa ... rõ ràng việc xóa thẻ <li> trong thẻ <ul> là đơn giản ... vì vậy, phương pháp duy nhất tôi nghĩ ra là giữ một danh sách phụ trong số các tệp đã xóa và tham chiếu đến nó trong quá trình tải lên ... do đó nếu tệp đó nằm trong danh sách tải lên, tôi chỉ bỏ qua và người dùng không ai khôn ngoan hơn.
cary abramoff

Khi bạn gán FileListđối tượng cho myReadWriteListbiến, nó sẽ thay đổi kiểu của nó từ Arraythành FileList, vì vậy đây không phải là một giải pháp.
adlr0

-2

Tôi chỉ thay đổi kiểu đầu vào thành văn bản và quay lại tệp: D


Đây được xem là một lời nhận xét
Ivan Kaloyanov

Làm thế nào được cho là làm việc? Làm thế nào bạn đạt được điều đó?
Ulrich Dohou
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.