Как установить объекты File и длину в объекте FileList, где файлы также отражаются в объекте FormData?

Можно установить .files собственностью <input type="file"> элемент к FileList например, из другого <input type="file"> элемент .files собственность или DataTransfer.files имущество. См. Make.files settable # 2866, Что происходит между загрузкой файла в HTML-форму и его отправкой?,

FileList объект имеет Symbol.iterator свойство, которое мы можем использовать, чтобы установить File объект, который является итеративным, однако .files.length по-прежнему настроен на 0 и прохождение <form> имеющий <input type="file"> установить где .files устанавливается с использованием вышеуказанного подхода дает File объект, имеющий .size установлен в 0,

Как установить File в FileList и установить .length из 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

1 ответ

Решение

Редактировать:

Как доказано OP, в одном из их существований есть способ сделать это...

Конструктор DataTransfer (в настоящее время поддерживается только Blink и FF> = 62) должен создать изменяемый FileList (в настоящее время chrome всегда возвращает новый FileList, но для нас это не имеет значения), доступный через DataTransferItemList.

Если я не ошибаюсь, в настоящее время это единственный специализированный способ сделать это, но в Firefox была ошибка в их реализации конструктора ClipboardEvent, когда тот же DataTransferItemList был установлен и установлен в режим чтения / записи, который позволял обходить ситуацию. для FF < 62. Я не уверен в своей интерпретации спецификаций, но я считаю, что она не должна быть доступна нормально).

Итак, способ, с помощью которого 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">

Это открытие привело к этому новому предложению сделать объекты FileList изменяемыми по умолчанию, так как больше нет смысла не делать этого.


Предыдущий (устаревший) ответ

Ты не можешь Объекты 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]); 
}

Другие вопросы по тегам