72

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.

5 Answers 5

117

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

11 Comments

I couldn't find a solution anywhere for so long, and you come almost instantly with the correct answer. Thank you very much! :)
Where do you append the file here? Is it missing in this code sample?
@ElectRocnic — 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).
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 :)
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 ?
|
21

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.

1 Comment

but the backend receives blob as the string of object like '[object Object]'.
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:

Comments

1

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

Comments

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

3 Comments

My header fields were already correctly set. The problem was Content-Type in the payload.
yes precisely in the request it is written text/plain when there should be multipart/form-data
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.

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.