19 Mar 2018

This article will take about 2 minutes to read.
26 Comments

Today I learned:

To upload files using fetch and FormData is supported in IE10+. you must not set Content-Type header.

const fileInput = document.querySelector('#your-file-input') ;
const formData = new FormData();

formData.append('file', fileInput.files[0]);

const options = {
  method: 'POST',
  body: formData,
  // If you add this, upload won't work
  // headers: {
  //   'Content-Type': 'multipart/form-data',
  // }
};

fetch('your-upload-url', options);

Problem I had

My API wrapper class has default content type header set to:
'Content-Type': 'application/json'

So I thought, to upload files using FormData, it would be enough to override it with:
'Content-Type': 'multipart/form-data'

But alas, it didn’t work, server couldn’t parse the files I was uploading. I’ve wasted about half an hour, and then noticed that simple HTML form was setting something else:

Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryIn312MOjBWdkffIM

It had this boundary thing I didn’t know anything about.

Then I started searching around the internet and found the solution. To set the correct boundary, I had to explicitly delete Content-Type header. In that case browser will set the correct boundary itself.

Adding this line solved it.

// Remove 'Content-Type' header to allow browser to add
// along with the correct 'boundary'
delete options.headers['Content-Type'];

Explanation

What is boundary and why I had to delete the header?

Multipart form allow transfer of binary data, therefore server needs a way to know where one field’s data ends and where the next one starts.

That’s where boundary comes in. It defines a delimiter between fields we are sending in our request (similar to & for GET requests). You can define it yourself, but it is much easier to let browser do it for you.

Example payload:

------WebKitFormBoundaryIn312MOjBWdkffIM
Content-Disposition: form-data; name="file"; filename="my-image.jpg"
Content-Type: image/jpeg


------WebKitFormBoundaryIn312MOjBWdkffIM
Content-Disposition: form-data; name="field"

imagePortrait
------WebKitFormBoundaryIn312MOjBWdkffIM--

That’s why I had to manually delete existing header, as it didn’t contain boundary, and server was unable to parse the file correctly.

Read more about it on this StackOverflow question and MDN.

Category
JavaScript

Comments (26)

Sam
26 Jun 2018, 03:28 AM

Thank you so much! Spent hours wrapping my brain around this till I read this article!

Yuzong
13 Jul 2018, 05:45 PM

This is what I’ve been looking for. Thanks!

Aecio
13 Sep 2018, 07:49 PM

Super util. I tried everything until I delete it and it worked. Thanks

Mohammad
25 Sep 2018, 01:35 AM

Thank you so much!

so much!

so much!

so much!

so much!

so much!

so much!

I Spent more than 2 days searching for posible problem because the post request succeed with Postman and failed when i use Angular 6 HttpClient and Django Rest Framework

then at 2:30 am i found your article!

i hope that you add more search key for exp HttpClient of Angular and Django Rest Famework so when some novice faces that problem they can find answers

you’re the best

Stanko
26 Sep 2018, 07:13 AM

You made my day, thank you! :) Glad I could help.

Katie
26 Sep 2018, 08:36 PM

Thank you! This was a huge help!

Barbara
16 Nov 2018, 05:33 PM

This FINALLY solved the issue I’d been having for hours, thank you so much!!!

Jorge
13 Dec 2018, 06:05 AM

Please help i have trying to upload a form data with fetch and doesn’t work. With postman works great but with fetch it doesnt.

Stanko
13 Dec 2018, 08:57 AM

Hello Jorge, It is really hard to help you with almost no information on the problem. Try searching for the exact error you are getting. To me, it sounds like a CORS issue, or maybe you are sending wrong headers. Good luck!

Suzanne
06 Feb 2019, 11:51 PM

After spending 3 hours beating my head against this wall I found your post. Problem solved, thank you so much.

Evgenii
14 Feb 2019, 08:12 AM

Great solution! Thank you!!!

Chap
11 Mar 2019, 10:39 AM

It doesn’t work for me. When I try to delete the content-type field, it always give me error, it seems the field is not even defined.

Input:

delete options.headers['Content-Type'];

Output:

Uncaught TypeError: Cannot convert undefined or null to object
Stanko
11 Mar 2019, 11:01 AM

Hello Chap,

That means options.headers is null or undefined. You probably have to check if it is defined before trying to delete it’s property.

Something like:

if (options && options.headers) {
delete options.headers['Content-Type'];
}

Cheers!

Thankful User
14 Mar 2019, 12:54 PM

I would have never been able to find out about this on my own, you’ve saved me hours of searching, thank you !!

karan
04 Apr 2019, 06:53 AM

hey, where exactly should we put this?

Cavdy
15 May 2019, 08:50 PM

Thanks so much. I have been on this for close to 7 hours figuring out why it’s not working

Phil
04 Jun 2019, 07:39 PM

delete options.headers[‘Content-Type’];

where do we put this?

Stanko
04 Jun 2019, 08:25 PM

There is a code sample in the first part of the post. You just need to remove “Content-Type” from options that you pass to fetch. Hope that helps!

Lakshay
05 Jun 2019, 02:23 PM

Hi, my upload is working, the issue i am facing is my backend is sending a map as response after this upload but the fetch response.ok always come false. please help here. struck in this from long time.

Bog
11 Jul 2019, 07:55 AM

I love you, my dude

abe
19 Jul 2019, 05:48 PM

I’m quite new to Express. Could you elaborate where this code goes “delete options.headers[‘Content-Type’];”. Thanks

Sergio
21 Sep 2019, 10:40 PM

Just saved my life

Serkan Atasagun
23 Sep 2019, 10:04 AM

You’ve saved my day. Thanks so much!

JL
24 Sep 2019, 11:49 PM

Saved the day.

PLR
14 Oct 2019, 10:03 PM

Thank you, you saved my evening!

Walter
28 Nov 2019, 10:44 AM

Thank you man. You saved my day. :)

Leave a comment

Sending failed, please try again.
Thank you! Your comment is sent. Please note that all of the comments go through moderation.