web-dev-qa-db-fra.com

Comment définir les objets File et la propriété length sur l'objet FileList où les fichiers sont également reflétés sur l'objet FormData?

Il est possible de définir la propriété .files De l'élément <input type="file"> Sur un FileList à partir par exemple d'une autre propriété de l'élément <input type="file">.files Ou DataTransfer.files. Voir Rendre les fichiers. # 2866 paramétrables , Que se passe-t-il entre le téléchargement d'un fichier dans un formulaire HTML et sa soumission? .

FileList objet a une propriété Symbol.iterator que nous pouvons utiliser pour définir un objet File qui est itérable, mais .files.length est toujours défini sur 0 et en passant un <form> ayant <input type="file"> défini où le .files est défini en utilisant l'approche ci-dessus, un objet File ayant .size Réglé sur 0.

Comment définir File sur FileList et définir .length De FileList sur le nombre de fichiers défini, où les fichiers sont définis sur FormData() objet?

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
26
guest271314

Modifier:

Comme prouvé par OP , dans l'un de leur Gist , il existe en fait un moyen de le faire ...

Le constructeur DataTransfer (actuellement uniquement pris en charge par Blink, et FF> = 62 ), devrait créer une FileList mutable (Chrome renvoie actuellement toujours une nouvelle FileList, mais cela n'a pas vraiment d'importance pour nous), accessible via DataTransferItemList.

Si je ne me trompe pas, c'est actuellement la seule façon de procéder, mais Firefox avait un bug dans leur implémentation du constructeur de ClipboardEvent , où le même DataTransferItemList était et défini sur le mode lecture/écriture qui permettait une solution de contournement pour FF <62. Je ne suis pas sûr de mon interprétation des spécifications, mais je pense qu'elle ne devrait pas être accessible normalement).

Ainsi, la manière guest271314 trouvée pour définir des fichiers arbitraires sur une FileList est la suivante:

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">

Cette découverte a conduit à ceci nouvelle proposition pour rendre les objets FileList mutables par défaut, car il n'y a plus de raison de ne pas le faire.


Réponse précédente (obsolète)

Tu ne peux pas. Les objets FileList ne peuvent pas être modifiés par des scripts *.

Vous pouvez uniquement échanger la FileList d'une entrée vers une autre FileList, mais vous ne pouvez pas la modifier *.
(* Sauf pour vider avec input.value = null).

Et vous ne pouvez pas non plus créer FileList à partir de zéro, seulement DataTransfer objets qui ne peuvent pas être créés non plus, et input[type=file] Créera de tels objets.

Pour vous montrer que même lorsque vous définissez une input[type=file] FileList sur celle d'une autre entrée, aucune nouvelle FileList n'est créée:

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 et j'ai presque oublié la partie FormData, que je n'ai pas vraiment compris pour dire la vérité ...

Donc, si je comprends bien, tout ce dont vous avez besoin est simplement 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]); 
}
38
Kaiido