I have an API endpoint to which I must send a multipart HTTP request, composed of two parts, file
(a file system file) and data
(a JSON object).
After some research I found out how to do a multipart request in AngularJS:
$http({
method: 'POST',
url: url,
headers: {
'Content-Type': 'multipart/form-data'
},
data: {
data: model,
file: file
},
transformRequest: customFormDataObject
});
1) The customFormDataObject
function initially had this form:
customFormDataObject formDataObject = function (data, headersGetter) {
var fd = new FormData();
angular.forEach(data, function (value, key) {
fd.append(key, value);
});
var headers = headersGetter();
delete headers['Content-Type'];
return fd;
};
The outcome of this implementation is that the individual parts of the request do not have a contentType
set to them.
2) After reading some more (https://stackoverflow.com/a/24535293/689216) I tried using a Blob for this, the customFormData
object looking like this (a bit of a mess, basically the first part will be of contentType
application/json
, the second one image/png
):
customFormDataObject = function (data, headersGetter) {
var fd = new FormData();
var contentType = 'application/json';
angular.forEach(data, function (value, key) {
fd.append(key, new Blob([JSON.stringify(value)], {
type: contentType
}));
contentType = 'image/png';
});
var headers = headersGetter();
delete headers['Content-Type'];
return fd;
};
This second approach sets the correct contentType
for each part of the request, but it does not set any values for the parts.
Basically what happens is with 1) the correct values are set in the multiparts, but the contentType
's are not set. With 2) the contentType
's are set, but not the values for the multiparts.
Am I missing something? Is this functionality not supposed to work like this?
Thanks!
The easiest way to upload files in Angular:
var fd = new FormData();
fd.append('file', file);
fd.append('data', 'string');
$http.post(uploadUrl, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
})
.success(function(){
})
.error(function(){
});
Absolutely essential are the following two properties of the config object:
transformRequest: angular.identity
overrides Angular's default serialization, leaving our data intact.
headers: {'Content-Type': undefined }
lets the browser detect the correct Content-Type
as multipart/form-data
, and fill in the correct boundary.
Nothing else worked for me! Courtesy of Lady Louthan's wonderful blogpost.
Have you tried something like this:
$httpProvider.defaults.transformRequest = function(data) {
if (data === undefined){
return data;
}
var formData = new FormData();
for (var key in data){
if(typeof data[key] == 'object' && !(data[key] instanceof File)){
formData.append(key, JSON.stringify(data[key]));
}else{
formData.append(key, data[key]);
}
}
return formData;
};
I just solve exactly the same problem.
After some tests, I had this situation that you've described:
Basically what happens is with 1) the correct values are set in the multiparts, but the contentType's are not set. With 2) the contentType's are set, but not the values for the multiparts.
To fix this problem, I had to use Blob and Post Ajax instead of $http Post.
It seems that $http does not work correctly in this case.
Code:
var formData = new FormData();
var blobId = new Blob([100], { 'type':'text/plain' });
formData.append('testId', blobId);
var blobImage = fileService.base64ToBlob(contentToDecode, 'image/jpeg');
formData.append('file', blobImage, 'imagem' + (i + 1) + '.jpg');
Request:
$.ajax({
url: url,
data: formData,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(response) {
deferred.resolve(response);
$rootScope.requestInProgress = false;
},
error: function(error) {
deferred.reject(error);
$rootScope.requestInProgress = false;
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With