Upload Multiple Files using the Fetch API

OpenJavaScript 0

Last updated: February 12, 2023.

Need to upload more than one file?

You may initially think that you could extend a POST request by placing multiple files in an array, setting this as the payload:

// This solution works:
fetch('https://httpbin.org/post', {
    method: "POST",
    body: file,
})

// This one does not:
fetch('https://httpbin.org/post', {
    method: "POST",
    body: [file1, file2], // Posts as "[object File],[object File]"
})

But you cannot send an array as a payload because you can only send text data. You could use JSON.stringify() if the data in the array can be represented in JSON format. But that isn’t the case for File objects.

Instead, you should append each file to a FormData object. This object is the equivalent of what is sent when you rely on HTML to send a form.

Just like in HTML, a FormData object will take care of file processing to make sure they are string-encoded when sent.

You can append files to a FormData object manually or, if files are embedded in a form, by passing the form element into the FormData() constructor as an argument.

For example, below files are appended manually to a FormData object before sending using formData.append():

<input type="file">
<input type="file">

<button>Upload</button>

<script>

const btn = document.querySelector('button');

btn.addEventListener('click', () => {

    // Get all inputs in DOM with attribute type of value "file":
    const fileInputs = document.querySelectorAll('input[type="file"]');
    
    // Push the file for each input into an array:
    const files = [];
    fileInputs.forEach((fileInput) => files.push(fileInput.files[0]));

    // Append each file in files array to FormData object:
    const fd = new FormData();
    for (const [index, file] of files.entries()) {
        fd.append(`file${index}`, file, file.name);
        // Arguments:
        // (1) Reference name on formData object
        // (2) File
        // (3) Set file name on file itself (optional)
    }

    // Upload the FormData object:
    fetch('https://httpbin.org/post', {
        method: "POST",
        body: fd,
    })
    .then(res => res.json())
    .then(res => console.log(res))

})

</script>

You do not need to set a 'Content-Type' header when sending a FormData object because this will be done automatically. Doing so manually can cause an error.

And here is an example of creating a FormData object that contains all data within a <form> element.

But beware this gotcha: only form elements with a name attribute will included on the FormData object!

<form>

    <input type="file" name="file1">
    <input type="file" name="file2">

    <button type="submit">Upload</button>

</form>

<script>

const btn = document.querySelector('button');
const form = document.querySelector('form');

form.addEventListener('submit', (e) => {
    e.preventDefault(); // Prevents HTML submission

    // Create FormData object, passing in form element:
    const fd = new FormData(form);

    // Upload the FormData object:
    fetch('https://httpbin.org/post', {
        method: "POST",
        body: fd,
    })
    .then(res => res.json())
    .then(res => console.log(res))

})

</script>

When using this method, the reference name for each file on the FormData object will be the value of the name attribute of each file input in HTML. The file name will be the name the user uploaded it with.

Related posts