javascript 如何在 FileList 对象中设置 File 对象和长度属性,其中文件也反映在 FormData 对象中?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/47119426/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to set File objects and length property at FileList object where the files are also reflected at FormData object?
提问by guest271314
It is possible to set .filesproperty of <input type="file">element to a FileListfrom for example a different <input type="file">element .filesproperty or DataTransfer.filesproperty. See Make .files settable #2866, What happens between uploading a file to an HTML form and submitting it?.
可以将元素的.files属性设置<input type="file">为FileList来自例如不同的<input type="file">元素.files属性或DataTransfer.files属性。请参阅Make .files settable #2866,将文件上传到 HTML 表单和提交之间会发生什么?.
FileListobject has a Symbol.iteratorproperty which we can use to set a Fileobject which is iterable, however the .files.lengthis still set to 0and passing a <form>having <input type="file">set where the .filesis set using the above approach yields a Fileobject having .sizeset to 0.
FileListobject 有一个Symbol.iterator属性,我们可以用它来设置一个File可迭代的对象,但是.files.length仍然设置为0并传递一个<form>具有<input type="file">集,其中.files使用上述方法设置的File对象会产生一个.size设置为的对象0。
How to set the Fileat FileListand set .lengthof FileListto the number of files set, where the files are set at FormData()object?
如何将FileatFileList和 set .lengthof设置为设置的FileList文件数,其中文件设置为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
回答by Kaiido
Edit:
编辑:
As proven by OP, in one of their gist, there is actually a way to do it...
正如OP所证明的那样,在他们的一个要点中,实际上有一种方法可以做到这一点......
The DataTransfer constructor(currently only supported by Blink, and FF >= 62), should create a mutable FileList (chrome currently always return a new FileList, but it doesn't really matter for us), accessible through the DataTransferItemList.
该数据传递构造函数(目前只有闪烁的支持,以及FF> = 62),应该创建一个可变的文件清单(目前铬总是返回一个新的文件清单,但它其实并不重要,对我们来说),通过DataTransferItemList访问。
If I'm not mistaken, this is currently the only specs-wise way to do it, but Firefox had a bugin their implementation of the ClipboardEvent constructor, where the same DataTransferItemList was and set to the mode read/writewhich allowed a workaround for FF < 62. I am not sure of my interpretation of the specs, but I believe it should not be accessible normally).
如果我没记错的话,这是目前唯一符合规范的方法,但是 Firefox在ClipboardEvent 构造函数的实现中有一个错误,其中相同的 DataTransferItemList 被设置为允许解决方法的读/写模式对于 FF < 62。我不确定我对规范的解释,但我相信它不应该正常访问)。
So the way guest271314found to set arbitrary files on a FileList is as follow:
所以guest271314发现在FileList上设置任意文件的方式如下:
const dT = new ClipboardEvent('').clipboardData || // Firefox < 62 workaround exploiting https://bugzilla.mozilla.org/show_bug.cgi?id=1422655
new DataTransfer(); // specs compliant (as of March 2018 only Chrome)
dT.items.add(new File(['foo'], 'programmatically_created.txt'));
inp.files = dT.files;
<input type="file" id="inp">
This discovery has lead to this new Proposalto make FileList objects mutable by default, since there is no point anymore to not do it.
这一发现导致了这个新的提案,即默认情况下使 FileList 对象可变,因为没有必要再不这样做了。
Previous (outdated) answer
以前的(过时的)答案
You can't. FileList objects cannot be modified by scripts*.
你不能。FileList 对象不能被脚本修改*。
You can only exchange the FileList of an input to an other FileList, but you can't modify it*.
(*Except for emptying with input.value = null).
您只能将输入的 FileList 交换到另一个 FileList,但不能修改它*。
(*除了用 清空input.value = null)。
And you can't either create FileList from scratch, only DataTransferobjects which cannot be created either, and input[type=file]will create such objects.
并且您不能从头开始创建 FileList,只能创建无法创建的DataTransfer对象,并且input[type=file]将创建此类对象。
To show you that even when setting an input[type=file]FileList to an other input's one no new FileList is created:
向您展示,即使将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">
Oh And I almost forgot the FormData part, that I don't really understood to say the truth...
哦,我差点忘记了 FormData 部分,我真的不明白说实话......
So if I got it ok, all you need is simply FormData.append():
所以如果我没问题,你只需要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]);
}

