FileList에서 파일을 제거하는 방법


111

HTML5를 사용하여 드래그 앤 드롭 방식의 업로드 웹 애플리케이션을 구축하고 있으며 파일을 div에 드롭하고 당연히 FileList 를 제공하는 dataTransfer 객체를 가져옵니다 .

이제 일부 파일을 제거하고 싶지만 방법이나 가능 여부를 모르겠습니다.

가급적이면 FileList에서 삭제하고 싶습니다. 나는 그들을 위해 아무 소용이 없습니다. 그러나 그것이 가능하지 않다면 대신 FileList와 상호 작용하는 코드에서 검사를 작성해야합니까? 번거로운 것 같습니다.


궁금한 점이 있습니다. 왜 이렇게 하시겠습니까? 사용자가 선택한 (일부) 파일에 대해 왜 "내가 쓸모가 없습니다"라고 말합니까?
— Marcel Korpel 2010

23
사용자가 업로드하기 전에 파일을 제거 할 수 있도록하는 것이 더 좋습니다. 원래 20 개를 선택했는데 실제로 14 번째 항목을 업로드하지 않기로 결정했다면 해당 항목을 제거 할 수 없으며 처음부터 다시 시작해야합니다 (약간의 고통입니다). 내가 보지 못하는 보안 문제가 없다면 FileList를 읽기 전용으로 만드는 것은 나쁜 감독이라고 생각합니다.
— Rafael

입력의 FileList에서 직접 파일을 삭제하는 것은 보안 문제이지만 파일 업로드 대화 상자를 닫은 직후 해당 FileList를 복제 한 다음이 복제본을 수정하여 ajax를 통해 게시 할 때 사용할 수 있습니다.
— alex_1948511

답변:


147

선택한 파일 중 여러 개만 삭제하려면 할 수 없습니다. 파일 API는 작업은 초안 메모를 포함하는 연결된 :

HTMLInputElement인터페이스 [HTML5]을 갖는 판독 전용 FileList 속성을 [...]
[강조 광산]

HTML 5 Working Draft의 일부를 읽고 Common inputelement API를 발견했습니다 . 다음 과 같이 개체 의 속성을 빈 문자열 로 설정 하여 전체 파일 목록을 삭제할 수 있습니다 .valueinput

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

BTW, 웹 응용 프로그램에서 파일 사용 기사 도 흥미로울 수 있습니다.


1
속성이 읽기 전용이라고해서 해당 속성이 가리키는 개체를 변경할 수 없다는 의미 는 아닙니다 . 가능하다면 FileList를 조작 할 수 있습니다. 이는 새로운 FileList를 할당 할 수 없음을 의미합니다.
— Robin Berjon 2013 년

1
@RobinBerjon Chrome은 ´readonly´ 속성을 무시하는 것처럼 보이지만 FireFox는 쓰기 작업을 허용하지 않습니다. 불행히도 FileList를 조작 하라는 제안 은 FireFox에서도 작동하지 않습니다.
— borisdiakur 2013

1
만 length읽기 전용이라고 생각합니다. 스플 라이스로 항목을 삭제하려고하는데 Chrome에서 실패합니다.
— zhiyelee

추가 할 방법이 있습니까?
— 가로등

1
@streetlight 사이트 소유자가 사용자의 컴퓨터에서 업로드 할 파일을 결정할 수 있다면 이는 엄청난 보안 취약점 이 될 것 입니다.
— Marcel Korpel 2015-08-27

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)간단하다
— 무하마드 Umer

1
@Muhammad Umer-감사합니다. 더 간단하고 대체 답변으로 나열된다는 데 동의합니다. 그러나 지원해야하는 브라우저와 Array.from ()을 사용하기 위해 pollyfill이 필요한지 여부에 따라 다릅니다. 참조 : stackoverflow.com/a/36810954/943435
— Roberto

실제로 FileList를 어떻게 수정합니까? 이 새 배열을 입력에 할당 fileDialog.files = fileBuffer 하시겠습니까?
— eozzy

@ 3zzy-FileList를 수정할 수 있지만 최신 브라우저에서만 가능합니다. 자세한 내용은이 SO 질문을 참조하십시오. stackoverflow.com/a/47522812/943435
— Roberto

22

항상 인기있는 브라우저 (Chrome, Firefox, Edge, Safari 9 이상에서도 작동)를 대상으로하거나 폴리 필을 사용할 수있는 경우 다음 Array.from()과 같이 사용하여 FileList를 배열로 전환 할 수 있습니다 .

let fileArray = Array.from(fileList);

그러면 File다른 배열처럼 s 배열을 처리하기 쉽습니다 .


완전한! IE 지원에 대해 알고 있습니까? 아니면 polyfill에 대한 링크를 공유 할 수 있습니까?
— Serhii Matrunchyk

나는 그것을 시도하지 않았지만 이것은 첫 번째 Google 결과입니다.) github.com/mathiasbynens/Array.from
— adlr0

그것은 당신이 처리 할 수 fileArray없습니다 fileList.
— VipinKundal

12

우리는 HTML5 영역에 있기 때문에 이것이 나의 해결책입니다. 요점은 파일을 FileList에 두는 대신 Array에 푸시 한 다음 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."));

아약스가 유일한 방법이라고 생각합니까?
— Muhammad Umer

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를 통해 보내시겠습니까?
— Muhammad Umer

1

이 작업을 수행하는 더 우아한 방법이있을 수 있지만 여기에 내 해결책이 있습니다. Jquery 사용

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

기본적으로 입력 값을 계산합니다. 그것을 복제하고 이전 복제 대신 복제하십시오.


1

이것은 일시적인 것이지만이 방법으로 해결 한 것과 동일한 문제가있었습니다. 제 경우에는 XMLHttp 요청을 통해 파일을 업로드했기 때문에 formdata 추가를 통해 FileList 복제 데이터를 게시 할 수있었습니다. 기능은 원하는만큼 여러 파일을 끌어서 놓거나 선택할 수 있다는 것입니다 (파일을 다시 선택해도 복제 된 FileList가 재설정되지 않음). (복제 된) 파일 목록에서 원하는 파일을 제거하고 xmlhttprequest를 통해 제출할 수 있습니다. 거기 왼쪽. 이것이 내가 한 일입니다. 여기에 첫 번째 게시물이므로 코드가 약간 지저분합니다. 죄송합니다. 아, 그리고 Joomla 스크립트 에서처럼 $ 대신 jQuery를 사용해야했습니다.

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

그 스타일. 나는 그들 중 일부를 표시해야했다!

.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에 있는지 간단히 확인할 수 있으며, 그렇지 않은 경우 해당 요소를 DB로 보내지 않습니다.


-1

읽기 전용 파일 목록 대신 배열을 만들고 사용할 수 있습니다.

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

그 후 내장 목록 대신 목록에 업로드하십시오. 작업중인 컨텍스트는 확실하지 않지만 내가 찾은 jquery 플러그인으로 작업 중이며 내가해야 할 일은 플러그인의 소스를 가져 와서 <script>태그를 사용하여 페이지에 넣는 것입니다 . 그런 다음 소스 위에 전역 변수로 작동하고 플러그인이 참조 할 수 있도록 배열을 추가했습니다.

그런 다음 참조를 교체하는 문제였습니다.

내장 목록이 읽기 전용 인 경우 드래그 앤 드롭을 다시 추가 할 수 있다고 생각합니다. 그러면 드롭 된 파일을 목록에 어떻게 가져올 수 있습니까?

:))


4
너무 빨리 썼습니다 .... 파일 목록과 같도록 var를 설정하는 순간 읽기 전용 문제가 다시 발생하는 것 같습니다. 따라서 제가 선택한 것은 두 배이고 약간 고통 스럽지만 효과적입니다. 업로드 할 파일의 가시적 인 목록과 여기에서 사용자가 제거 할 수 있습니다 ... 분명히 <ul> 태그에서 <li> 태그를 제거하는 것은 간단합니다 ... 그래서 제가 생각 해낸 유일한 방법은 보조 목록을 유지하는 것입니다 삭제 된 파일을 삭제하고 업로드 과정에서 참조하십시오. 따라서 파일이 업로드 목록에 있으면 건너 뛰고 사용자는 현명하지 않습니다.
— cary abramoff 2011 년

FileList객체를 myReadWriteList변수에 할당하면 유형이에서 Array로 변경 FileList되므로 이것은 해결책이 아닙니다.
— adlr0

당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.