353

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 */);
1
  • 3
    official document works for me after trying some answers failed: developer.mozilla.org/en-US/docs/Web/API/Fetch_API/… , something can confirm: 1. need wrap file in FromData; 2. doesn't need to declare Content-Type: multipart/form-data in request header Commented Aug 22, 2018 at 14:09

11 Answers 11

395

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
})
Sign up to request clarification or add additional context in comments.

12 Comments

You don't need to wrap the file contents in a FormData object if all you're uploading is the file (which is what the original question wants). fetch will accept input.files[0] above as its body parameter.
If you have a PHP backend handling the file upload you will want to wrap the file in a FormData so that the $_FILES array is properly populated.
I also noticed that Google Chrome would not show the file in the request payload without the FormData part for some reason. Seems like a bug in Google Chrome's Network panel.
This should really be the correct answer. The other way works also but is more convoluted
how would you read this file from let's say Express backend. As the file is not sent as form data. It is instead sent as just the file object. Does express-fileupload or multer parse such payloads?
|
228

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

7 Comments

Why does this example include Content-Type headers but another answer says to omit them when sending files with Fetch API? Which one is it?
Do NOT set Content-Type. I spent a lot of time trying to make it work and then found this article saying to not set it. And it works! muffinman.io/uploading-files-using-fetch-multipart-form-data
how would you read this file from let's say Express backend. As the file is not sent as form data. It is instead sent as just the file object. Does express-fileupload or multer parse such payloads?
Just in case if someone needs to download file from one place and upload it to another: const fileFetch = await fetch(fileDownloadUrl); const fileBlob = await fileFetch.blob(); upload(fileBlob)
Anyone else only getting an empty object on the server side?
|
191

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

7 Comments

THIS! Very important! Don't use your own content-type with fetch on multipart. I had no idea why my code does not work.
This is gold! I wasted 1 hr not understanding this. Thanks for sharing this tip
This is very important info which is not captured in the MDN Fetch docs.
In chrome it will default to text/plain. This won't work if you have set some custom headers of yours.
|
55

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

2 Comments

Note that explicitly passing file.name as the filename isn't necessary; per the MDN docs on FormData.append, the default name for File objects already is the file's filename.
@MarkAmery that's good to know. Is there any difference in the payload on the Request or server in regards to providing a name?
36

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.

4 Comments

This is the simplest answer but how does body: myInput.files[0] cause to the amount of bytes held in memory at the client side ?
I would expect that with this solution the browser would be sensible enough to stream the file and not require it to be read into memory, @bhantol, but I haven't gone out of my way to find out (either empirically or by delving into the spec). If you wanted to confirm, you could try (in each of the major browsers) using this approach to upload a 50GB file or something, and see whether your browser tries to use too much memory and gets killed.
Did not work for me. express-fileupload failed to parse the request stream. But FormData works like a charm.
@attacomsian At a glance, it looks to me like express-fileupload is a serverside library for handling multipart/form-data requests that contain files, so yeah, it's not compatible with this approach (which just directly sends the file as the request body).
22

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);
});

4 Comments

Using FormData will only work if the server is expecting form data. If the server wants a raw file as the body of the POST the accepted answer is correct.
@Clyde what do you mean? I thought it was the client that decides what type of content to send to the server?
Yes, but the client and server have to agree on what content can be sent and how it is encoded. It is certainly possible to write server code to accept either a raw POST body or FormData (the headers will say what encoding has been used by the client) but often the server will be expecting a specific encoding so you have to send content that matches that.
"does not ask to set the content type"? Or asks that you do not set the content type? The matter of whether or not to include Content-Type is already in question. Please take communication somewhat seriously.
12

It would be nice to add php endpoint example. So that is js:

const uploadinput = document.querySelector('#uploadinputid');
const uploadBtn = document.querySelector('#uploadBtnid');
uploadBtn.addEventListener('click',uploadFile);

async function uploadFile(){
    const formData = new FormData();
    formData.append('nameusedinFormData',uploadinput.files[0]);    
    try{
        const response = await fetch('server.php',{
            method:'POST',
            body:formData
        } );
        const result = await response.json();
        console.log(result);
    }catch(e){
        console.log(e);

    }
}

That is php:

$file = $_FILES['nameusedinFormData'];
$temp = $file['tmp_name'];
$target_file = './targetfilename.jpg';
move_uploaded_file($_FILES["image"]["tmp_name"], $target_file);

2 Comments

The question makes no assumption as to the backend techology used to upload the file.
Yet somehow it’s still helpful to others learning and reading
6

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

Comments

2

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.

Comments

2

Here is my code:

html:

const upload = (file) => {
    console.log(file);

    

    fetch('http://localhost:8080/files/uploadFile', { 
    method: 'POST',
    // headers: {
    //   //"Content-Disposition": "attachment; name='file'; filename='xml2.txt'",
    //   "Content-Type": "multipart/form-data; boundary=BbC04y " //"multipart/mixed;boundary=gc0p4Jq0M2Yt08jU534c0p" //  ή // multipart/form-data 
    // },
    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
  );

  //cvForm.submit();
};

const onSelectFile = () => upload(uploadCvInput.files[0]);

uploadCvInput.addEventListener('change', onSelectFile, false);
<form id="cv_form" style="display: none;"
										enctype="multipart/form-data">
										<input id="uploadCV" type="file" name="file"/>
										<button type="submit" id="upload_btn">upload</button>
</form>
<ul class="dropdown-menu">
<li class="nav-item"><a class="nav-link" href="#" id="upload">UPLOAD CV</a></li>
<li class="nav-item"><a class="nav-link" href="#" id="download">DOWNLOAD CV</a></li>
</ul>

1 Comment

From Review: Hi, please don't answer just with source code. Try to provide a nice description about how your solution works. See: How do I write a good answer?. Thanks
0

How to upload a single file on select using HTML5 fetch

<label role="button">
  Upload a picture
  <input accept="image/*" type="file" hidden />
</label>
const input = document.querySelector(`input[type="file"]`);

function upload() {
  fetch(uploadURL, { method: "PUT", body: input.files[0] });
}

input.addEventListener("change", upload); 

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.