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.