如何从 FileList 中删除文件

我正在使用 HTML5构建一个拖放到上传的 web 应用程序,我将文件拖放到一个 div 上,当然还获取了 dataTransfer 对象,这样就得到了 文件列表

现在我想删除一些文件,但我不知道怎么做,或者这是否可能。

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

250976 次浏览

如果只想删除所选文件中的几个,则不能删除。您链接到的 工作草案包含一个注释:

HTMLInputElement接口 [ HTML5]有一个 只读 FileList 属性,[ ... ]
[强调我的]

在阅读了一些 HTML 5工作草案之后,我偶然发现了 公共 input元素 API。似乎可以通过将 input对象的 value属性设置为空字符串来删除 完整的文件列表,如:

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

顺便说一下,文章 使用 Web 应用程序中的文件也可能是感兴趣的。

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

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

在这一点之后,您的上传对您的列表,而不是内置的列表。我不确定你工作的环境,但我正在使用我发现的一个 jquery 插件,我必须做的是把插件的源代码,并把它放在页面使用 <script>标签。然后在源代码的上面,我添加了我的数组,这样它就可以作为一个全局变量,并且插件可以引用它。

那就只需要换掉推荐信了。

我认为这将允许您再次添加拖放,如果内置的列表是只读的,那么如何才能得到删除文件到列表中?

:))

因为我们是在 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."));

我知道这是一个老问题,但它在搜索引擎排名很高的关于这个问题。

不能删除 文件列表对象中的属性,但至少在 Firefox 它们是可以改变的上不能。我解决这个问题的方法是将属性 IsValid=true添加到那些通过检查的文件中,将属性 IsValid=false添加到那些未通过检查的文件中。

然后我只是循环遍历列表,以确保只有具有 IsValid=true的属性被添加到 表格数据

这个问题已经得到了回答,但是我想分享一些信息,这些信息可能有助于其他人使用 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 + 中测试正常

可能有一种更优雅的方法来做到这一点,但这里是我的解决方案

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

基本上,你要清除输入的值,克隆它,然后用克隆代替原来的。

如果你的目标是常绿浏览器(Chrome,Firefox,Edge,但也可以在 Safari 9 + 中使用)或者你买得起一个填充器,你可以像这样使用 Array.from()把 FileList 变成一个数组:

let fileArray = Array.from(fileList);

然后就可以像处理其他数组一样处理 File数组了。

如果您有幸将文件发送到数据库的文章请求,并且您有要在 DOM 中发送的文件

您可以简单地检查文件列表中的文件是否存在于您的 DOM 中,当然,如果不存在,则不要将该元素发送给 de DB。

我已经找到了一个非常快速和简短的解决方案,在许多流行的浏览器(Chrome,Firefox,Safari)中进行了测试;

首先,必须将 FileList 转换为 Array

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

要删除特定的元素,请使用

newFileList.splice(index,1);

这是临时的,但是我有同样的问题,我用这种方法解决了。在我的例子中,我是通过 XMLHttp 请求上传文件的,所以我能够通过附加的 formdata 发布 FileList 克隆数据。这就是我所做的。这是我在这里的第一篇文章,所以代码有点混乱。对不起。啊,我不得不使用 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;
}

希望这个能帮上忙。

我只是将输入类型更改为文本,然后返回到文件: D

谢谢@Nicholas Anderson 简单而直接,这是您应用的代码,并使用 jquery 处理我的代码。

超文本标示语言。

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

译者:

self.$refs.inputFile.value = ''

由于 JavaScript FileList 是只读的,不能直接操作,

最佳方法

您必须循环遍历 input.files,同时将其与要删除的文件的 index进行比较。同时,您将使用 new DataTransfer()设置一个新的文件列表,其中不包括要从文件列表中删除的文件。

使用这种方法,input.files本身的值就会发生变化。

removeFileFromFileList(index) {
const dt = new DataTransfer()
const input = document.getElementById('files')
const { files } = input
  

for (let i = 0; i < files.length; i++) {
const file = files[i]
if (index !== i)
dt.items.add(file) // here you exclude the file. thus removing it.
}
  

input.files = dt.files // Assign the updates list
}

替代方法

另一个简单的方法是将 FileList 转换为数组,然后将其拼接起来。

但是这种方法不会改变 input.files

const input = document.getElementById('files')
// as an array, u have more freedom to transform the file list using array functions.
const fileListArr = Array.from(input.files)
fileListArr.splice(index, 1) // here u remove the file
console.log(fileListArr)

我意识到这是一个相当老的问题,但我使用一个 html 多文件选择上传队列任何数量的文件,可以有选择地删除在一个自定义用户界面之前提交。

将文件保存在下面这样的变量中:

let uploadedFiles = [];


//inside DOM file select "onChange" event
let selected = e.target.files[0] ? e.target.files : [];
uploadedFiles = [...uploadedFiles , ...selected ];
createElements();

“删除文件”创建 UI:

function createElements(){
uploadedFiles.forEach((f,i) => {


//remove DOM elements and re-create them here
/* //you can show an image like this:
*  let reader = new FileReader();
*  reader.onload = function (e) {
*    let url = e.target.result;
*    // create <img src=url />
*  };
*  reader.readAsDataURL(f);
*/


element.addEventListener("click", function () {
uploadedFiles.splice(i, 1);
createElements();
});


}
}

向服务器提交:

let fd = new FormData();
uploadedFiles.forEach((f, i) => {
fd.append("files[]", f);
});
fetch("yourEndpoint", {
method: "POST",
body: fd,
headers: {
//do not set Content-Type
}
}).then(...)

我是这么解决的

 //position  -> the position of the file you need to delete


this.fileImgs.forEach((item, index, object) => {
if(item.idColor === idC){
if(item.imgs.length === 1){
object.splice(index,1) }
else{
const itemFileImgs = [...item.imgs];
itemFileImgs.splice(position,1)
item.imgs = [...itemFileImgs]
}
}});
console.log(this.fileImgs)

enter image description here

我混合了许多开发人员的解决方案,达到了这个解决方案。 它在删除后更改原始数组列表,这意味着如果我们想要保存图像,那么我们可以这样做。

<script>
var images = [];
function image_select() {
var image = document.getElementById('image').files;
for (i = 0; i < image.length; i++) {
images.push({
"name" : image[i].name,
"url" : URL.createObjectURL(image[i]),
"file" : image[i],
})
}
document.getElementById('container').innerHTML = image_show();
}


function image_show() {
var image = "";
images.forEach((i) => {
image += `<div class="image_container d-flex justify-content-center position-relative">
<img src="`+ i.url +`" alt="Image">
<span class="position-absolute" onclick="delete_image(`+ images.indexOf(i) +`)">&times;</span>
</div>`;
})
return image;
}
function delete_image(e) {
images.splice(e, 1);
document.getElementById('container').innerHTML = image_show();


const dt = new DataTransfer()
const input = document.getElementById('image')
const { files } = input


for (let i = 0; i < files.length; i++) {
const file = files[i]
if (e !== i)
dt.items.add(file);
}


input.files = dt.files;
console.log(document.getElementById('image').files);
}
</script>

这是 HTML 代码

<body>
<div class="container mt-3 w-100">
<div class="card shadow-sm w-100">
<div class="card-header d-flex justify-content-between">
<h4>Preview Multiple Images</h4>
<form class="form" action="\{\{route('store')}}" method="post" id="form" enctype="multipart/form-data">
@csrf
<input type="file" name="image[]" id="image" multiple onchange="image_select()">
<button class="btn btn-sm btn-primary" type="submit">Submit</button>
</form>
</div>
<div class="card-body d-flex flex-wrap justify-content-start" id="container">


</div>
</div>
</div>
</body>

这是 CSS 代码

<style>
.image_container {
height: 120px;
width: 200px;
border-radius: 6px;
overflow: hidden;
margin: 10px;
}
.image_container img {
height: 100%;
width: auto;
object-fit: cover;
}
.image_container span {
top: -6px;
right: 8px;
color: red;
font-size: 28px;
font-weight: normal;
cursor: pointer;
}
</style>