Jump to content

Uploading files using 'fetch' and 'FormData'

Posted in JavaScript · 2 minutes read

Today I learned:

To upload files using fetch and FormDataFormData 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.

Comments (45)

5. February 2021
Mitra

exactly this is what I was looking for, thanks for sharing, it solve all my issue

19. January 2021
Luiz

Don't working for me.

26. December 2020
Phuc

I have spent one day for fixing this problem. I wish i could find your post sooner. Thank a lot Bro!

25. December 2020
ed

As the above folks mentioned, your post helped me quite a bit. I spent hours till I found your post with the solution. Thank you!

24. December 2020
Renato

That doesnt make sense

there is no Content-Type do delete after set options without Content-Type key

const options = { method: 'POST', body: formData }; delete options.headers['Content-Type'];

Its the same as just const options = { method: 'POST', body: formData };

22. October 2020
Paul

THank you so Much

22. October 2020
Dave

I can't thank you for enough for posting this solution. Never would have thought that specifying an accurate Content-Type header field would be an issue. Saved me hours!

10. September 2020
Jesús

Wow, thanks! man, i spend some time with that boundary problem, you got a new follower.

10. September 2020
Stanko

Indeed I’m :)

10. September 2020
calmp

You must be a Frank Zappa fan! :-)

30. June 2020
Jordi

I spent 3 hours because of that lil' contentType: 'multipart/form-data'. Luckilly I stumbled upon your post. Thank you so much

26. June 2020
Stanko

Hello Sanyukta, I think you just need to update the field name, from files to files[]. For multiple files, field needs to be an array.

Cheers!

26. June 2020
Sanyukta

Thank you. This post really helped. How do we upload multiple files? This is my code which does not work.

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

formData.append('file', fileField.files[0]); 
formData.append('file', fileField.files[1]); 
formData.append('file', fileField.files[2]); 

When I print the length of the fileField.files, it gives me as 1. where as I have uploaded 3 files using my html code.

How do we upload 3 files?

2. April 2020
Stanko

You are welcome! I had no idea this is an issue for so many people, glad I could help.

2. April 2020
Faysal Bsata

You are an angel from god. Thank you I've wasted a day and a half trying to diagnose the issue. Thank you

29. February 2020
Ramesh

You made my day! Thank you so much. I was having trouble in uploading photo in react native.

21. January 2020
HONGIL

Hey.

Thank you for your posting. My upload is now working.

you save my evening.

Cheers!

8. January 2020
Stanko

Hey Dave,

fetch accepts two params, url and options. So before calling it, make sure your options are not including Content-Type header.

To answer your question, delete Content-Type header right before you call fetch.

Cheers!

8. January 2020
Dave

Thank you for this article. I've been stumped on a file upload forever. I'm still new to development, so I have a question. Where exactly do I write the delete options.headers['Content-Type']; code? Inside the fetch request?

28. November 2019
Walter

Thank you man. You saved my day. :)

14. October 2019
PLR

Thank you, you saved my evening!

24. September 2019
JL

Saved the day.

23. September 2019
Serkan Atasagun

You’ve saved my day. Thanks so much!

21. September 2019
Sergio

Just saved my life

19. July 2019
abe

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

11. July 2019
Bog

I love you, my dude

5. June 2019
Lakshay

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.

4. June 2019
Stanko

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!

4. June 2019
Phil

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

where do we put this?

15. May 2019
Cavdy

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

4. April 2019
karan

hey, where exactly should we put this?

14. March 2019
Thankful User

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

11. March 2019
Stanko

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!

11. March 2019
Chap

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

14. February 2019
Evgenii

Great solution! Thank you!!!

6. February 2019
Suzanne

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

13. December 2018
Stanko

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!

13. December 2018
Jorge

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.

16. November 2018
Barbara

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

26. September 2018
Katie

Thank you! This was a huge help!

26. September 2018
Stanko

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

25. September 2018
Mohammad

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

13. September 2018
Aecio

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

13. July 2018
Yuzong

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

26. June 2018
Sam

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