I'm following Heroku's tutorial on direct uploads to Amazon S3.
After getting a signed request from AWS through the Node.js app, they use a "normal" XMLHttpRequest
to send the file.
This is their function:
function upload_file(file, signed_request, url){
var xhr = new XMLHttpRequest();
xhr.open("PUT", signed_request);
xhr.setRequestHeader('x-amz-acl', 'public-read');
xhr.onload = function() {
if (xhr.status === 200) {
document.getElementById("preview").src = url;
document.getElementById("avatar_url").value = url;
}
};
xhr.onerror = function() {
alert("Could not upload file.");
};
xhr.send(file);
}
Now, I'm working with Cordova and, since I don't get a File
object from the camera plugin, but only the file URI, I used Cordova's FileTransfer
to upload pictures to my Node.js app with multipart/form-data
and it worked fine.
However, I can't manage to make it work for Amazon S3.
Here's what I have:
$scope.uploadPhoto = function () {
$scope.getSignedRequest(function (signedRequest) {
if (!signedRequest)
return;
var options = new FileUploadOptions();
options.fileKey = 'file';
options.httpMethod = 'PUT';
options.mimeType = 'image/jpeg';
options.headers = {
'x-amz-acl': 'public-read'
};
options.chunkedMode = false;
var ft = new FileTransfer();
ft.upload($scope.photoURI, encodeURI(signedRequest.signed_request), function () {
// success
}, function () {
// error
}, options);
});
};
I've tried both chunkedMode = true
and chunkedMode = false
, but neither the success nor the error callback is called.
So, is there a way to upload a file to S3 with FileTransfer
?
Do I actually need the signed request or is it only necessary if I use XHR?
Any hint is appreciated.
I'm following Heroku's tutorial on direct uploads to Amazon S3.
After getting a signed request from AWS through the Node.js app, they use a "normal" XMLHttpRequest
to send the file.
This is their function:
function upload_file(file, signed_request, url){
var xhr = new XMLHttpRequest();
xhr.open("PUT", signed_request);
xhr.setRequestHeader('x-amz-acl', 'public-read');
xhr.onload = function() {
if (xhr.status === 200) {
document.getElementById("preview").src = url;
document.getElementById("avatar_url").value = url;
}
};
xhr.onerror = function() {
alert("Could not upload file.");
};
xhr.send(file);
}
Now, I'm working with Cordova and, since I don't get a File
object from the camera plugin, but only the file URI, I used Cordova's FileTransfer
to upload pictures to my Node.js app with multipart/form-data
and it worked fine.
However, I can't manage to make it work for Amazon S3.
Here's what I have:
$scope.uploadPhoto = function () {
$scope.getSignedRequest(function (signedRequest) {
if (!signedRequest)
return;
var options = new FileUploadOptions();
options.fileKey = 'file';
options.httpMethod = 'PUT';
options.mimeType = 'image/jpeg';
options.headers = {
'x-amz-acl': 'public-read'
};
options.chunkedMode = false;
var ft = new FileTransfer();
ft.upload($scope.photoURI, encodeURI(signedRequest.signed_request), function () {
// success
}, function () {
// error
}, options);
});
};
I've tried both chunkedMode = true
and chunkedMode = false
, but neither the success nor the error callback is called.
So, is there a way to upload a file to S3 with FileTransfer
?
Do I actually need the signed request or is it only necessary if I use XHR?
Any hint is appreciated.
Share Improve this question asked Jul 12, 2015 at 0:17 hornobsterhornobster 7361 gold badge7 silver badges20 bronze badges 3- I feel your pain. See my answer here, might help. stackoverflow./questions/30931146/… – derekdon Commented Aug 11, 2015 at 11:32
- I implemented a workaround just yesterday... I take the base64 data from the camera, decode it to a blob and send it with a normal XHR. But I'll try your solution because, if it works, it's definitely better – hornobster Commented Aug 11, 2015 at 15:31
- Update: it didn't work. I'll stick with the XHR. – hornobster Commented Aug 12, 2015 at 10:45
2 Answers
Reset to default 6I ended up with this function in Cordova:
$scope.uploadPhoto = function () {
$scope.getSignedRequest(function (signedRequest) {
if (!signedRequest)
return;
var options = new FileUploadOptions();
options.chunkedMode = false;
options.httpMethod = 'PUT';
options.headers = {
'Content-Type': 'image/jpeg',
'X-Amz-Acl': 'public-read'
};
var ft = new FileTransfer();
ft.upload($scope.photoURI, signedRequest.signedUrl, function () {
$scope.$apply(function () {
// success
});
}, function () {
$scope.$apply(function () {
// failure
});
}, options);
});
};
The important bits are setting the Content-Type
header, so that multipart/form-data
won't be used, and chunkedMode = false
to send the file with a single request.
EDIT: Removed changes to the plugin code which were, in hindsight, useless (outdated plugin).
Not able to add ment: Strange. Works for me using $cordovaFileTransfer.upload. I don't have the 'x-amz-acl': 'public-read' header. Also I don't use encodeURI on the signed url. Have you been able to debug it? See any errors? I used chrome://inspect and port forwarding to connect to my app running on the phone, so I was able to debug the response from Amazon. Might be another reason why it's failing.