如何在 FileList 对象中设置文件对象和长度属性,其中文件也反映在 FormData 对象中?
Posted
技术标签:
【中文标题】如何在 FileList 对象中设置文件对象和长度属性,其中文件也反映在 FormData 对象中?【英文标题】:How to set File objects and length property at FileList object where the files are also reflected at FormData object? 【发布时间】:2018-04-17 13:58:00 【问题描述】:可以将<input type="file">
元素的.files
属性设置为FileList
,例如不同的<input type="file">
元素.files
属性或DataTransfer.files
属性。见Make .files settable #2866、What happens between uploading a file to an html form and submitting it?。
FileList
对象有一个Symbol.iterator
属性,我们可以使用它来设置一个可迭代的File
对象,但是.files
.length
仍然设置为0
并传递一个<form>
<input type="file">
设置其中使用上述方法设置.files
会产生一个File
对象,其中.size
设置为0
。
如何将File
设置为FileList
并将FileList
的.length
设置为设置的文件数,其中文件设置在FormData()
对象?
const input = document.createElement("input");
const form = document.createElement("form");
const [...data] = [
new File(["a"], "a.txt")
, new File(["b"], "b.txt")
];
input.type = "file";
input.name = "files";
input.multiple = true;
// set `File` objects at `FileList`
input.files[Symbol.iterator] = function*()
for (const file of data)
yield file
;
;
form.appendChild(input);
const fd = new FormData(form);
for (const file of input.files)
console.log(file); // `File` objects set at `data`
for (const [key, prop] of fd)
// `"files"`, single `File` object having `lastModified` property
// set to a time greater than last `File` object within `data`
// at Chromium 61, only `"files"` at Firefox 57
console.log(key, prop);
console.log(input.files.length); // 0
【问题讨论】:
已在 FF57 中修复,请参阅 developer.mozilla.org/en-US/Firefox/Releases/57#DOM 【参考方案1】:编辑:
正如由 OP 证明的那样,在 their gist 之一中,实际上有一种方法可以做到这一点...
DataTransfer constructor(目前只支持 Blink,and FF >= 62),应该创建一个可变的 FileList(chrome 目前总是返回一个新的 FileList,但这对我们来说并不重要) ,可通过 DataTransferItemList 访问。
如果我没记错的话,这是目前唯一符合规范的方法,但是 Firefox 在他们的ClipboardEvent constructor 实现中有一个错误,其中相同的 DataTransferItemList 和设置为 read/write 模式,它允许 FF 我不确定我对规范的解释,但我认为它不应该正常访问)。子>
所以发现guest271314在FileList上设置任意文件的方式如下:
const dT = new DataTransfer();
dT.items.add(new File(['foo'], 'programmatically_created.txt'));
inp.files = dT.files;
<input type="file" id="inp">
这一发现导致new Proposal 默认情况下使 FileList 对象可变,因为不再这样做是没有意义的。
然而,虽然这实际上源自规范行为,但这更像是规范中的一个漏洞,仍应被视为一种 hack。不要在生产中使用它,而是更喜欢使用简单的 Array 和 FormData 来控制将哪些文件发送到服务器。
上一个(过时的)答案
你不能。 FileList 对象不能被脚本修改*。
您只能将输入的 FileList 交换到另一个 FileList,但不能修改它*。
(*input.value = null
清空除外)。
你也不能从头开始创建 FileList,只有 DataTransfer 也不能创建对象,input[type=file]
会创建这样的对象。
向您展示即使将 input[type=file]
FileList 设置为其他输入的,也不会创建新的 FileList:
var off = inp.cloneNode(); // an offscreen input
inp.onchange = e =>
console.log('is same before', inp.files === off.files);
off.files = inp.files; // now 'off' does have the same FileList as 'inp'
console.log('is same after', inp.files === off.files);
console.log('offscreen input FileList', off.files);
console.log('resetting the offscreen input');
off.value = null;
console.log('offscreen input FileList', off.files);
console.log('inscreen input FileList', inp.files);
<input type="file" id="inp">
哦,我差点忘记了 FormData 部分,我真的不明白说实话......
所以如果我没问题,你只需要FormData.append()
:
var fd = new FormData();
fd.append("files[]", new Blob(['a']), 'a.txt');
fd.append("files[]", new Blob(['b']), 'b.txt');
for(let pair of fd.entries())
console.log(pair[0], pair[1]);
【讨论】:
@SamuelLiew 新的部分仍然主要是一个hack。新规范(截至今天仅在 Blink 中实现)允许这样做,但这并不是设计使然,更多的是巧合。 FF 后备是滥用其实现中的一个小错误。因此,虽然这是一个有趣的 hack,可能是 lead to a real API,但我确实相信这个答案的第二部分仍然需要,而且,即使使用 FileList 构造函数,它仍然有效:你不会能够修改 FileList,只能创建新的。 @Kaiido 不幸的是,我们仍然无法在 Safari(可能还有 IE/Edge)中创建新的DataTransfer
,对吗?
@mb21 我最近没有在 Edge 中查看,但是是的,据我所知,只有 Chrome 已经开始实现 DataTransfer 构造函数,而 FF 漏洞利用仅适用于 FF。顺便说一句,您确实链接到的错误报告并没有真正提到我正在谈论的错误(我确实打开了一个,所以我将在编辑中添加一个链接),即使很高兴看到他们正在处理它。
我们仍然无法在 Safari、IE/Edge 中调用 DataTransfer
构造函数
DataTransfer 构造函数现在可以在基于 Chromium 的 Edge 中使用。以上是关于如何在 FileList 对象中设置文件对象和长度属性,其中文件也反映在 FormData 对象中?的主要内容,如果未能解决你的问题,请参考以下文章