18

I would like to upload multiple files using a post request of type multipart/form-data and for each file I need to know the file size (content-length) on the server side.

To construct the POST request in javascript I use a FormData object and append the File objects for the upload to it. This works fine, but only a Content-type header is added to each part, in addition to the Content-Disposition header, but no Content-length header, though this information is available from the individual File objects.

Is there a way to achieve that that Content-length headers are set for every part from the FormData object when sending the request?

Below is the code I use, including my work-around to the problem. It actually uses angular-js to send the request, but I think this is not relevant to the question.

var form = new window.FormData();

form.append('additional-field-1', new Blob(['some plain text'], {type : 'text/plain'}));

for (var file in fileList) {
    var fileObj = fileList[file];
    var count = 1 + parseInt(file, null);
    form.append('file-size-' + count, new Blob([fileObj.size], {type : 'text/plain'}));
    form.append('file-' + count, fileObj);
}

$http.post(url, form, {
    transformRequest: angular.identity,
    headers: {'Content-Type': undefined}
}).success(.....
Amareesh
  • 368
  • 2
  • 5
  • 19
user1587520
  • 3,777
  • 1
  • 21
  • 20
  • Added a bounty to this, been a pain for me as well. – James Feb 16 '16 at 20:03
  • I was gonna propose to use angular to form the request xD – RaidenF Feb 22 '16 at 11:53
  • 1
    Could you explain why you need this? Normally, the server-side code works out the size of each "part" in the multipart data by looking for the boundary delimiter, parsing the headers, etc - whatever is left is the data. Do you need to know the size of each part *before* you parse the body of each "part" for some reason? – sheltond Feb 22 '16 at 13:33
  • I don't this is possible. Angular uses native browser's functionality to serialize and send HTTP request. Content-Type and Content-Disposition are set by browser, not by Angular. – Volodymyr Usarskyy Feb 23 '16 at 14:49
  • @sheltond I want to stream the upload to a blob store which requires to specify the content length of the payload when starting the upload. – user1587520 Feb 23 '16 at 15:54
  • 1
    Well, https://tools.ietf.org/html/rfc2046 says "The only header fields that have defined meaning for body parts are those the names of which begin with 'Content-'". This implies that it's a valid thing to add a content-length header, but it doesn't seem like the HTML5 FormData class allows you to do it. That class is only really doing the multipart encoding, so you could use an alternative, but I haven't been able to find one with a brief search that does what you want. – sheltond Feb 23 '16 at 17:20
  • In my usage I am using angular $resource to form the requests to a multipart/mixed form that is posting multiple images to a server, to get an idea of the timeframe it is going to take to upload and the status of the transmitting right now it uses a Content-length header for each part which the entire post is being hand generated in java, I am trying to move to an all JS solution but formData doesn't appear to handle this type of request. – James Feb 23 '16 at 17:21

1 Answers1

5

I don't believe there is a way to actually add a custom header for each form data element. However why don't you add it to the content disposition header, as part of the file name:

data = new FormData();
data.append('additional-field-1', new Blob(['some plain text'], {type : 'text/plain'}));

for (var i = 0; i< $( '#file' )[0].files.length; i++) {
   var fileObj = $( '#file' )[0].files[i];
   data.append( '{ size : ' + fileObj.size + ' }' , $( '#file' [0].files[i], $( '#file' )[0].files[i].name );
}

I'm not sure how you are handling this on the server, but the request would look like this:

------WebKitFormBoundarysZxMHYOzMkqDmOvR
Content-Disposition: form-data; name="additional-field-1"; filename="blob"
Content-Type: text/plain


------WebKitFormBoundarysZxMHYOzMkqDmOvR
Content-Disposition: form-data; name="{ size : 22984 }"; filename="MatrixArithmetic.vshost.exe"
Content-Type: application/x-msdownload


------WebKitFormBoundarysZxMHYOzMkqDmOvR
Content-Disposition: form-data; name="{ size : 187 }"; filename="MatrixArithmetic.vshost.exe.config"
Content-Type: application/xml


------WebKitFormBoundarysZxMHYOzMkqDmOvR--
Wawy
  • 6,259
  • 2
  • 23
  • 23
Low Flying Pelican
  • 5,974
  • 1
  • 32
  • 43
  • So the problem I am running into is trying to replicate functionality where a java developer hard coded the header information into a raw post and doesn't seem that to be likely to be able to replicate here. – James Feb 22 '16 at 18:54