[javascript] How do I upload a file with the JS fetch API?

I am still trying to wrap my head around it.

I can have the user select the file (or even multiple) with the file input:

<form>
  <div>
    <label>Select file to upload</label>
    <input type="file">
  </div>
  <button type="submit">Convert</button>
</form>

And I can catch the submit event using <fill in your event handler here>. But once I do, how do I send the file using fetch?

fetch('/files', {
  method: 'post',
  // what goes here? What is the "body" for this? content-type header?
}).then(/* whatever */);

This question is related to javascript fetch-api

The answer is


This is a basic example with comments. The upload function is what you are looking for:

// Select your input type file and store it in a variable
const input = document.getElementById('fileinput');

// This will upload the file after having read it
const upload = (file) => {
  fetch('http://www.example.net', { // Your POST endpoint
    method: 'POST',
    headers: {
      // Content-Type may need to be completely **omitted**
      // or you may need something
      "Content-Type": "You will perhaps need to define a content-type here"
    },
    body: file // This is your file object
  }).then(
    response => response.json() // if the response is a JSON object
  ).then(
    success => console.log(success) // Handle the success response object
  ).catch(
    error => console.log(error) // Handle the error response object
  );
};

// Event handler executed when a file is selected
const onSelectFile = () => upload(input.files[0]);

// Add a listener on your input
// It will be triggered when a file will be selected
input.addEventListener('change', onSelectFile, false);

The accepted answer here is a bit dated. As of April 2020, a recommended approach seen on the MDN website suggests using FormData and also does not ask to set the content type. https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

I'm quoting the code snippet for convenience:

const formData = new FormData();
const fileField = document.querySelector('input[type="file"]');

formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);

fetch('https://example.com/profile/avatar', {
  method: 'PUT',
  body: formData
})
.then((response) => response.json())
.then((result) => {
  console.log('Success:', result);
})
.catch((error) => {
  console.error('Error:', error);
});

To submit a single file, you can simply use the File object from the input's .files array directly as the value of body: in your fetch() initializer:

const myInput = document.getElementById('my-input');

// Later, perhaps in a form 'submit' handler or the input's 'change' handler:
fetch('https://example.com/some_endpoint', {
  method: 'POST',
  body: myInput.files[0],
});

This works because File inherits from Blob, and Blob is one of the permissible BodyInit types defined in the Fetch Standard.


If you want multiple files, you can use this

var input = document.querySelector('input[type="file"]')

var data = new FormData()
for (const file of input.files) {
  data.append('files',file,file.name)
}

fetch('/avatars', {
  method: 'POST',
  body: data
})

Jumping off from Alex Montoya's approach for multiple file input elements

const inputFiles = document.querySelectorAll('input[type="file"]');
const formData = new FormData();

for (const file of inputFiles) {
    formData.append(file.name, file.files[0]);
}

fetch(url, {
    method: 'POST',
    body: formData })

I've done it like this:

var input = document.querySelector('input[type="file"]')

var data = new FormData()
data.append('file', input.files[0])
data.append('user', 'hubot')

fetch('/avatars', {
  method: 'POST',
  body: data
})

Here is my code:

html:

_x000D_
_x000D_
const upload = (file) => {_x000D_
    console.log(file);_x000D_
_x000D_
    _x000D_
_x000D_
    fetch('http://localhost:8080/files/uploadFile', { _x000D_
    method: 'POST',_x000D_
    // headers: {_x000D_
    //   //"Content-Disposition": "attachment; name='file'; filename='xml2.txt'",_x000D_
    //   "Content-Type": "multipart/form-data; boundary=BbC04y " //"multipart/mixed;boundary=gc0p4Jq0M2Yt08jU534c0p" //  ? // multipart/form-data _x000D_
    // },_x000D_
    body: file // This is your file object_x000D_
  }).then(_x000D_
    response => response.json() // if the response is a JSON object_x000D_
  ).then(_x000D_
    success => console.log(success) // Handle the success response object_x000D_
  ).catch(_x000D_
    error => console.log(error) // Handle the error response object_x000D_
  );_x000D_
_x000D_
  //cvForm.submit();_x000D_
};_x000D_
_x000D_
const onSelectFile = () => upload(uploadCvInput.files[0]);_x000D_
_x000D_
uploadCvInput.addEventListener('change', onSelectFile, false);
_x000D_
<form id="cv_form" style="display: none;"_x000D_
          enctype="multipart/form-data">_x000D_
          <input id="uploadCV" type="file" name="file"/>_x000D_
          <button type="submit" id="upload_btn">upload</button>_x000D_
</form>_x000D_
<ul class="dropdown-menu">_x000D_
<li class="nav-item"><a class="nav-link" href="#" id="upload">UPLOAD CV</a></li>_x000D_
<li class="nav-item"><a class="nav-link" href="#" id="download">DOWNLOAD CV</a></li>_x000D_
</ul>
_x000D_
_x000D_
_x000D_


An important note for sending Files with Fetch API

One needs to omit content-type header for the Fetch request. Then the browser will automatically add the Content type header including the Form Boundary which looks like

Content-Type: multipart/form-data; boundary=—-WebKitFormBoundaryfgtsKTYLsT7PNUVD

Form boundary is the delimiter for the form data


The problem for me was that I was using a response.blob() to populate the form data. Apparently you can't do that at least with react native so I ended up using

data.append('fileData', {
  uri : pickerResponse.uri,
  type: pickerResponse.type,
  name: pickerResponse.fileName
 });

Fetch seems to recognize that format and send the file where the uri is pointing.