23

I have this input of type "file", and I want to change its files list. Example:

<input type = "file" id = "fileinput" />
<script type = "text/javascript">
  document.getElementById("fileinput").files = [10];
</script>

The problem is that the fileinput element's files list is not set. How do I do it?

6
  • You can't. It's read only for obvious security reasons. Commented Apr 12, 2011 at 9:00
  • I see, so then I can't allow it to upload multiple files with multiple clicks then?
    – dave
    Commented Apr 12, 2011 at 9:02
  • @dave HTML5 allow multiple selection of files but you still can't decide what will be those files. Commented Apr 12, 2011 at 9:03
  • I see. So there is no way via javascript or scripting that I can alter the state of file input?
    – dave
    Commented Apr 12, 2011 at 9:04
  • @dave you can alter anything but the selected file(s) of the control.. Commented Apr 12, 2011 at 9:06

3 Answers 3

57

It's indeed impossible to add a local file to a file input that the user didn't request; however, there is a way to add Blob or File objects to or remove specific files from a file input.


To change the files in the file input, you have to replace the fileInput.files with another FileList object.

The problem is that FileList objects are immutable and have no constructor exposed to JS.

The only way of creating custom FileLists is by abusing DataTransfer, an API designed for transferring files using drag-and-drop or through the clipboard.

It is supported in all mainstream browsers, but not in IE, and only since version 14.1 in Safari. You can check it here.

You can create a DataTransfer object by invoking its constructor without arguments:

const dataTransfer = new DataTransfer()

The created DataTransfer object has a files property, which is, fortunately, a FileList containing all files the DataTransfer has.

But then how to add files to the DataTransfer?

Each DataTransfer has a DataTransferItemList associated with it (accessible through its items property) that has the methods for adding (and removing) items to (and from) the DataTransfer.

To add a file, you have to call the add method on it and pass a File object.

Like so:

dataTransfer.items.add(file)

If you have data that is not in a File object (e.g. a Blob or ArrayBuffer of bytes), you can use the File constructor, passing the data, the filename, and optionally an object with type and lastModified properties to it:

const file = new File([blob], 'file.txt', {type: 'text/plain', lastModified: modificationDate})

So, to sum up, we have something like this:

new DataTransfer()
 |
 v
DataTransfer 
 |  |
 |  +--[ .items ]---> DataTransferItemList
 |                      |
 +--[ .files ]------+   +---[ .add(file) ]---> DataTransferItem
                    |               ^
                    v               |
 +--[ .files ] <-- FileList         |
 |                                  +--- File <--- new File([data], filename, options)
 |                                                            ^
 |                                                            |
<input type="file">      Blob ----------+---------------------+
                         ArrayBuffer ---+
                         string --------+

I'm sure this is quite messy right now, but let's see some code!

If you wanted to create a file named hello.txt containing Hello world! and set the input to contain this file, here's how can you do it:

const fileInput = document.getElementById('fileInput')

const dataTransfer = new DataTransfer()

const file = new File(['Hello world!'], 'hello.txt', {type: 'text/plain'})

dataTransfer.items.add(file)

fileInput.files = dataTransfer.files
<p>Notice that the file input contains "hello.txt" now: </p>
<input type="file" id="fileInput" />

But instead of replacing the files, how can you edit the list of files already in the input?

  1. Create a DataTransfer
  2. Add all existing files from the input except the ones you want to remove
  3. Add the files you want to add
  4. Replace the files in the input with the ones in the DataTransfer

See also this answer of mine about that topic.

For doing this, I've created a pair of functions for getting and setting the file list from an array of Files, so the transformations can be performed on arrays instead of doing complicated stuff with DataTransfers:

function getFiles(input){
  const files = new Array(input.files.length)
  for(let i = 0; i < input.files.length; i++)
    files[i] = input.files[i]
  return files
}

function setFiles(input, files){
  const dataTransfer = new DataTransfer()
  for(const file of files)
    dataTransfer.items.add(file)
  input.files = dataTransfer.files
}

You can use them like this:

function getFiles(input){
  const files = new Array(input.files.length)
  for(let i = 0; i < input.files.length; i++)
    files[i] = input.files[i]
  return files
}

function setFiles(input, files){
  const dataTransfer = new DataTransfer()
  for(const file of files)
    dataTransfer.items.add(file)
  input.files = dataTransfer.files
}

const fileInput = document.querySelector('#fileInput')

document.querySelector('#removeFirst').addEventListener('click', () => {
  const files = getFiles(fileInput)
  
  files.shift()
  
  setFiles(fileInput, files)
})
document.querySelector('#removeLastModified').addEventListener('click', () => {
  const files = getFiles(fileInput)
  
  let latest = 0, latestIndex
  for(let i = 0; i < files.length; i++)
    if(files[i].lastModified > latest){
      latest = files[i].lastModified
      latestIndex = i
    }
  files.splice(latestIndex, 1)  
  
  setFiles(fileInput, files)
})
document.querySelector('#addFile').addEventListener('click', () => {
  const files = getFiles(fileInput)
  
  const newFiles = getFiles(document.querySelector('#addFileInput'))
  files.push(...newFiles)
  
  setFiles(fileInput, files)
})
document.querySelector('#addRandomHello').addEventListener('click', () => {
  const files = getFiles(fileInput)
  
  const newFile = new File(['Hello world!'], 'hello.txt', {type: 'text/plain'})
  const index = Math.floor(Math.random() * (files.length + 1))
  files.splice(index, 0, newFile)
  
  setFiles(fileInput, files)
})
Hint: hover over the file input to see the list of all files in it <br>
<input type="file" id="fileInput" multiple ><br><br>
<button id="removeFirst">Remove first file</button><br>
<button id="removeLastModified">Remove latest modified file</button><br>
<button id="addFile">Append file from this input</button> <input type="file" id="addFileInput" /><br>
<button id="addRandomHello">Add a hello.txt file to a random place</button><br>

10
  • Take note of the bowser support on the DataTransfer constructor if you choose to use this answer.
    – Manstie
    Commented Oct 17, 2021 at 9:52
  • @Manstie Well, yes, it's not supported in IE, but support seems fair otherwise. IE support is often dropped in present-day projects. Anyway, I'll mention that.
    – FZs
    Commented Oct 17, 2021 at 13:48
  • 1
    None of this is a criticism to your answer, just warning people if they go to use it.
    – Manstie
    Commented Oct 18, 2021 at 7:25
  • 1
    I got that, I'm just trying to add everything to my answer so that people don't have to read the comments to get this info.
    – FZs
    Commented Oct 18, 2021 at 11:54
  • 1
    @camb I posted this as a late answer and the OP isn't active anymore, that's why it's not accepted. Regarding [...input.files], that would also work in JS, but it requires FileList to be iterable, which isn't specified by the HTML standard, so I chose to avoid it (although Chromium and FF both implement FileList.prototype[@@iterator]).
    – FZs
    Commented Jun 1, 2023 at 10:54
19

For security reasons, browsers prevent javascript from changing the files which will be uploaded: only the user can select files via the user interface. This is to prevent an evil script to upload /etc/passwd, for example, without the user knowing.

The one exception is that calling "reset" on the form will clear the file or filelist, but you can never add to programmatically.

6
  • 5
    So how would i add more files to the file input's filelist when clicking it multiple times?
    – dave
    Commented Apr 12, 2011 at 9:04
  • 1
    @odrm you can't add local real files to file list, but there's nothing wrong with adding File or Blob. I could imagine an userscript that would do that and be helpful. Commented Dec 3, 2014 at 2:14
  • @Tomáš Zato I don't think you could, because how the browser would know the origin of the file, if it is a programmatically created or stolen from the user? Commented Mar 14, 2017 at 21:29
  • @atas you cannot steal blob from the user programmatically unless they submit it already. Commented Mar 14, 2017 at 21:55
  • 1
    @atas And what does that have to do with security? You could always override form's onsubmit event and create the request using AJAX. Commented Mar 15, 2017 at 11:36
-2

What you want is using the multiple attribute on the input element. That way, in newer browsers user will be able to select multiple files to upload.

Not the answer you're looking for? Browse other questions tagged or ask your own question.