sending file and json in POST multipart/form-data request with axios
Asked Answered
B

5

66

I am trying to send a file and some json in the same multipart POST request to my REST endpoint. The request is made directly from javascript using axios library as shown in the method below.

doAjaxPost() {
    var formData = new FormData();
    var file = document.querySelector('#file');

    formData.append("file", file.files[0]);
    formData.append("document", documentJson);

    axios({
        method: 'post',
        url: 'http://192.168.1.69:8080/api/files',
        data: formData,
    })
    .then(function (response) {
        console.log(response);
    })
    .catch(function (response) {
        console.log(response);
    });
}

However, the problem is when I inspect the request in chrome developer tools in the network tab, I find no Content-Type field for document, while for file field Content-Type is application/pdf (I'm sending a pdf file).

Request shown in network inspector

On the server Content-Type for document is text/plain;charset=us-ascii.

Update:

I managed to make a correct request via Postman, by sending document as a .json file. Though I discovered this only works on Linux/Mac.

Belemnite answered 9/6, 2018 at 12:14 Comment(0)
B
112

To set a content-type you need to pass a file-like object. You can create one using a Blob.

const obj = {
  hello: "world"
};
const json = JSON.stringify(obj);
const blob = new Blob([json], {
  type: 'application/json'
});
const data = new FormData();
data.append("document", blob);
axios({
  method: 'post',
  url: '/sample',
  data: data,
})
Biotic answered 9/6, 2018 at 12:37 Comment(11)
I couldn't find a solution anywhere for so long, and you come almost instantly with the correct answer. Thank you very much! :)Belemnite
Where do you append the file here? Is it missing in this code sample?Waitress
@Waitress — The entire answer is about generating the JSON file in memory and appending that. I didn't repeat the logic for reading a file from a file input because the question isn't about that (and the code to demonstrate that is already in the question).Biotic
Thanks I got it running anyways. The only missing line was for me formData.append("file", file) which I added and it works for me :)Waitress
Very useful. Thanks. On the server, how do I unpack the 'document'? I see that the blob is uploaded as well as the file. I don't want the blob to be uploaded. I just need to unpack 'document'. How do I achieve that ?Les
@Les — The blob is the file. You read it the same way you read any other file from a multipart form request style POST request (how you do that depends on your choice of server-side programming language and form data handling library)Biotic
@Biotic yea, figured that. I am using node server side. So the logic was like this: add blob -> upload -> read blob -> parse json -> add/update data -> delete uploaded blobLes
Thanx mate! quick, clear, easy. That's how I like it! :PUrena
this creates a filename though, and many backends will interpret this as a file - which is not.Bein
@Bein — While the browser might not have read a file to get the data, it used the data to create a file and send it to the backend. As far as the backend is concerned, is is a file. Treating it as a file will work. The question is specifically about a backend which expects a file to be sent to it.Biotic
How do I read the Blob data in django?Inkstand
F
20

Try this.

doAjaxPost() {
    var formData = new FormData();
    var file = document.querySelector('#file');

    formData.append("file", file.files[0]);
    // formData.append("document", documentJson); instead of this, use the line below.
    formData.append("document", JSON.stringify(documentJson));

    axios({
        method: 'post',
        url: 'http://192.168.1.69:8080/api/files',
        data: formData,
    })
    .then(function (response) {
        console.log(response);
    })
    .catch(function (response) {
        console.log(response);
    });
}

You can decode this stringified JSON in the back-end.

Farmergeneral answered 30/10, 2020 at 18:10 Comment(1)
but the backend receives blob as the string of object like '[object Object]'.Farmergeneral
U
5

You cant set content-type to documentJson, because non-file fields must not have a Content-Type header, see HTML 5 spec 4.10.21.8 multipart form data.

And there`s two way to achieve your goals:

Unreconstructed answered 22/9, 2022 at 1:17 Comment(0)
D
0

you only need to add the right headers to your request

axios({
  method: 'post',
  url: 'http://192.168.1.69:8080/api/files',
  data: formData,
  header: {
            'Accept': 'application/json',
            'Content-Type': 'multipart/form-data',
          },
    })
Dogleg answered 9/6, 2018 at 12:43 Comment(3)
My header fields were already correctly set. The problem was Content-Type in the payload.Belemnite
yes precisely in the request it is written text/plain when there should be multipart/form-dataDogleg
This will override the Content-Type for the multipart data, discarding the boundary parameter and breaking the ability of the server to parse it. It won't have any effect on the Content-Type for the "document" part of that multipart data.Biotic
N
0

we can use new Blob() and JSON.stringify() method to stringify json object and append to FormData, file data we can able to directly append to form data in the following way

function multiPart(payload) {
    const formData = new FormData()
    if (payload?.files.length > 0) {
        let attachments = []
        payload?.files.map(item => {
            item.attachmentList = item.attachments.map(d => d.name)
            item.attachments = item.attachments.map(d => new File([ d ], `${item.accountId}#${d.name}`, { type: d.type })) // if we need to update file name 
            attachments = attachments.concat(item.attachments)
            item.attachments = item.attachmentList.map(d => ({name: d}))
            return item
        })
        attachments.forEach(d => { formData.append('fileNames', d) })
    }

    const stringfiedJson = new Blob([ JSON.stringify(payload) ], { type: 'application/json' }) // important to mention type
    formData.append('postJson', stringfiedJson)

    return formData
}
const modifiedPayload = multiPart(payload)


axios({
    method: 'post',
    url: 'http://localhost:8080/api/user-data',
    data: modifiedPayload
})
Norland answered 10/1, 2024 at 8:38 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.