Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set File objects and length property at FileList object where the files are also reflected at FormData object?

It is possible to set .files property of <input type="file"> element to a FileList from for example a different <input type="file"> element .files property or DataTransfer.files property. See Make .files settable #2866, What happens between uploading a file to an HTML form and submitting it?.

FileList object has a Symbol.iterator property which we can use to set a File object which is iterable, however the .files .length is still set to 0 and passing a <form> having <input type="file"> set where the .files is set using the above approach yields a File object having .size set to 0.

How to set the File at FileList and set .length of FileList to the number of files set, where the files are set at FormData() object?

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
like image 740
guest271314 Avatar asked Nov 05 '17 07:11

guest271314


People also ask

What is FileList used for?

FileList is a command-line utility that generates a CSV file listing the contents of a given directory. By default, the list includes the file name, size, and path, as well as the last-access, last-modified, and creation dates, etc. You can easily import all results to a spreadsheet or database.

How do I upload a file to FormData?

You can upload the selected file by creating a FormData class and passing it to Axios' post() function. const input = document. querySelector('#my-input'); const formData = new FormData(); formData. append('myFile', input.

Is FormData an object?

The FormData object lets you compile a set of key/value pairs to send using XMLHttpRequest . It is primarily intended for use in sending form data, but can be used independently from forms in order to transmit keyed data.

How do you append files in input type file multiple before uploading?

You could add a new <input type="file"> whenever you finished uploading the previous files and hide the previous input. This way you keep adding a new input every time you want to add more files and prevent the previous input from being overwritten. And it doesn't require you to use AJAX.


1 Answers

Edit:

As proven by OP, in one of their gist, there is actually a way to do it...

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.

If I'm not mistaken, this is currently the only specs-wise way to do it, but Firefox had a bug in their implementation of the ClipboardEvent constructor, where the same DataTransferItemList was and set to the mode read/write which 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).

So the way guest271314 found to set arbitrary files on a FileList is as follows:

const dT = new DataTransfer(); 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 Proposal to make FileList objects mutable by default, since there is no point anymore to not do it.

However, while this is actually derived from specs behavior, this is more of a loop-hole in the specs and should still be considered an hack. Do not use this in production and instead prefer a simple Array and FormData to control which files are sent to the server.


Previous (outdated) answer

You can't. FileList objects cannot be modified by scripts*.

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).

And you can't either create FileList from scratch, only DataTransfer objects which cannot be created either, and input[type=file] will create such objects.

To show you that even when setting an input[type=file] FileList to an other input's one no new FileList is created:

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

So if I got it ok, all you need is simply 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]);  }
like image 135
Kaiido Avatar answered Sep 21 '22 13:09

Kaiido