如何在文件也反映在 FormData 对象的 FileList 对象处设置 File 对象和长度 属性?
How to set File objects and length property at FileList object where the files are also reflected at FormData object?
可以将 <input type="file">
元素的 .files
属性 设置为 FileList
来自例如不同的 <input type="file">
元素 .files
属性 或 DataTransfer.files
属性。参见 Make .files settable #2866, 。
FileList
对象有一个 Symbol.iterator
属性,我们可以用它来设置一个可迭代的 File
对象,但是 .files
.length
仍然设置为 0
并传递具有 <input type="file">
设置的 <form>
使用上述方法设置 .files
会产生一个 File
具有 .size
设置为 0
.
如何设置FileList
的File
和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
编辑:
OP证明,在 their gist 之一中,实际上有一种方法可以做到...
DataTransfer constructor (currently only supported by Blink, and FF >= 62), 应该创建一个可变的 FileList (chrome 目前总是 return 一个新的 FileList,但这对我们来说并不重要) ,可通过 DataTransferItemList 访问。
如果我没记错的话,这是目前唯一符合规范的方法,但是 Firefox 在 ClipboardEvent constructor 的实现中有一个 bug ,其中相同的 DataTransferItemList 被设置为模式 read/write,这允许 FF < 62 的解决方法。我不确定我对规范的解释,但我认为它应该不能正常访问)。
所以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 对象默认可变,因为没有必要再不这样做了。
然而,虽然这实际上是从规范行为中派生出来的,但这更像是规范中的一个漏洞,仍应被视为黑客行为。不要在生产中使用它,而是更喜欢使用简单的 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]);
}
可以将 <input type="file">
元素的 .files
属性 设置为 FileList
来自例如不同的 <input type="file">
元素 .files
属性 或 DataTransfer.files
属性。参见 Make .files settable #2866,
FileList
对象有一个 Symbol.iterator
属性,我们可以用它来设置一个可迭代的 File
对象,但是 .files
.length
仍然设置为 0
并传递具有 <input type="file">
设置的 <form>
使用上述方法设置 .files
会产生一个 File
具有 .size
设置为 0
.
如何设置FileList
的File
和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
编辑:
OP证明,在 their gist 之一中,实际上有一种方法可以做到...
DataTransfer constructor (currently only supported by Blink, and FF >= 62), 应该创建一个可变的 FileList (chrome 目前总是 return 一个新的 FileList,但这对我们来说并不重要) ,可通过 DataTransferItemList 访问。
如果我没记错的话,这是目前唯一符合规范的方法,但是 Firefox 在 ClipboardEvent constructor 的实现中有一个 bug ,其中相同的 DataTransferItemList 被设置为模式 read/write,这允许 FF < 62 的解决方法。我不确定我对规范的解释,但我认为它应该不能正常访问)。
所以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 对象默认可变,因为没有必要再不这样做了。
然而,虽然这实际上是从规范行为中派生出来的,但这更像是规范中的一个漏洞,仍应被视为黑客行为。不要在生产中使用它,而是更喜欢使用简单的 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]);
}