How do I POST with multipart form data using fetch?
Solution 1
You're setting the Content-Type
to be multipart/form-data
, but then using JSON.stringify
on the body data, which returns application/json
. You have a content type mismatch.
You will need to encode your data as multipart/form-data
instead of json
. Usually multipart/form-data
is used when uploading files, and is a bit more complicated than application/x-www-form-urlencoded
(which is the default for HTML forms).
The specification for multipart/form-data
can be found in RFC 1867.
For a guide on how to submit that kind of data via javascript, see here.
The basic idea is to use the FormData object (not supported in IE < 10):
async function sendData(url, data) {
const formData = new FormData();
for(const name in data) {
formData.append(name, data[name]);
}
const response = await fetch(url, {
method: 'POST',
body: formData
});
// ...
}
Per this article make sure not to set the Content-Type
header. The browser will set it for you, including the boundary
parameter.
Solution 2
I was recently working with IPFS and worked this out. A curl example for IPFS to upload a file looks like this:
curl -i -H "Content-Type: multipart/form-data; boundary=CUSTOM" -d $'--CUSTOM\r\nContent-Type: multipart/octet-stream\r\nContent-Disposition: file; filename="test"\r\n\r\nHello World!\n--CUSTOM--' "http://localhost:5001/api/v0/add"
The basic idea is that each part (split by string in boundary
with --
) has it's own headers (Content-Type
in the second part, for example.) The FormData
object manages all this for you, so it's a better way to accomplish our goals.
This translates to fetch API like this:
const formData = new FormData()
formData.append('blob', new Blob(['Hello World!\n']), 'test')
fetch('http://localhost:5001/api/v0/add', {
method: 'POST',
body: formData
})
.then(r => r.json())
.then(data => {
console.log(data)
})
varad
Updated on January 06, 2021Comments
-
varad over 3 years
I am fetching a URL like this:
fetch(url, { mode: 'no-cors', method: method || null, headers: { 'Accept': 'application/json, application/xml, text/plain, text/html, *.*', 'Content-Type': 'multipart/form-data' }, body: JSON.stringify(data) || null, }).then(function(response) { console.log(response.status) console.log("response"); console.log(response) })
My API expects the data to be of
multipart/form-data
so I am usingcontent-type
of this type... But it is giving me a response with status code 400.What's wrong with my code?
-
Matt Pengelly over 5 yearsNote about the above method, DO NOT supply headers if you do it using FormData because it will override the boundary thats automatically set.
-
Dragos Strugar over 5 yearsThanks @MattPengelly! How to set custom headers like Authorization then?
-
RobertMcReed about 5 years@DragosStrugar you can still set headers (Authorization included), just don't manually set the Content-Type header if you are using FormData.
-
caot almost 5 yearsDO NOT supply headers with 'Content-Type' if it's using FormData.
-
konsumer almost 5 yearsIn the curl example, you need it. In the
FormData
example you don't need it, because the browser sends that header for you & also manages all the mime-boundries, which is the point of this solution. -
Brian Peterson over 4 yearsI think supplying formData directly to the body like this is a much more elegant approach if you don't need to send additional data
-
konsumer over 4 years@Brian Peterson: I agree. also it's not too bad even if you have other info to add: formData.set("field","value") for each other value (do it in a loop.)
-
Jnana about 4 yearsconst fd = new FormData(); // File to upload. fd.append('file', fileToUpload); fd.append('jsondatakey', 'jsondatavalue'); With this you will be able to send file along with some json data in body.
-
DAVE over 3 yearsWhat if I need an Authorization?