58

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.

pavlee
  • 828
  • 1
  • 12
  • 19

4 Answers4

103

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,
})
Quentin
  • 914,110
  • 126
  • 1,211
  • 1,335
  • 1
    I couldn't find a solution anywhere for so long, and you come almost instantly with the correct answer. Thank you very much! :) – pavlee Jun 09 '18 at 12:56
  • 7
    Where do you append the file here? Is it missing in this code sample? – ElectRocnic Feb 20 '19 at 18:21
  • 1
    @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). – Quentin Feb 20 '19 at 19:50
  • 7
    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 :) – ElectRocnic Feb 20 '19 at 20:33
  • 1
    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 ? – abhisek Mar 15 '19 at 06:56
  • 1
    @abhisek — 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) – Quentin Mar 15 '19 at 08:21
  • @Quentin 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 blob – abhisek Mar 20 '19 at 15:45
  • Thanx mate! quick, clear, easy. That's how I like it! :P – Despertaweb Nov 14 '19 at 08:55
  • this creates a filename though, and many backends will interpret this as a file - which is not. – Pithikos Aug 13 '20 at 11:41
  • @Pithikos — 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. – Quentin Aug 13 '20 at 21:54
  • How do I read the Blob data in django? – cgitosh Mar 02 '22 at 03:18
15

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.

David Buck
  • 3,752
  • 35
  • 31
  • 35
3

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:

LeoYang
  • 31
  • 1
2

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',
          },
    })
eth3rnit3
  • 687
  • 1
  • 5
  • 23
  • My header fields were already correctly set. The problem was `Content-Type` in the payload. – pavlee Jun 09 '18 at 12:59
  • yes precisely in the request it is written text/plain when there should be multipart/form-data – eth3rnit3 Jun 09 '18 at 13:04
  • 8
    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. – Quentin Jun 09 '18 at 13:28