I'm trying to use Dropzone.js to upload directly to Google Cloud Storage using a Signed URL. I've managed to override the upload URL for each file added to Dropzone. Chrome dev tools says a PUT
request is occurring but I inevitably receive a HTTP 400 error in response.
Here's my Dropzone.js config
Dropzone.options.myAwesomeDropzone = {
url: '/',
uploadMultiple: false,
method: 'PUT',
parallelUploads: 1,
uploadMultiple: false,
header: '',
autoProcessQueue: false,
autoDiscover: false,
maxFiles: 1,
acceptedFiles: 'image/*,video/*',
accept: function(file, done) {
var self = this;
$.post('/api/v1/signed_file_upload', {key: window.apiKey, name: file.name, type: file.type}, function(data) {
if(data.success) {
file.uploadURL = data.data;
done()
setTimeout(function() {
self.processFile(file)
}, 0)
} else {
done(data.message)
}
})
},
init: function() {
var self = this;
this.on('processing', function(file) {
self.options.url = file.uploadURL
})
this.on('sending', function(file, xhr, formData) {
var _send = xhr.send
xhr.send = function() {
_send.call(xhr, file)
}
});
}
};
My signed URL has the following structure:
https://www.googleapis.com/upload/storage/v1/b/{bucket_name}/o/{object_name}.png?GoogleAccessId=xxx@xxx.iam.gserviceaccount.com&Expires=1521610072&Signature=xxx
Chrome Dev Tools shows this for the upload request:
I inevitably receive an HTTP 400 response. Sometimes the body is empty and sometimes it returns a JSON object saying
{
"error": {
"errors": [
{
"domain": "global",
"reason": "badContent",
"message": "Unsupported content with type: image/jpeg"
}
],
"code": 400,
"message": "Unsupported content with type: image/jpeg"
}
}
My signature generating function is
function storage_url($file_name, $bucket_name = '', $content_type = '', $method = 'PUT', $duration = 3000) {
$expires = time() + $duration;
$signature = '';
$to_sign = ($method . "\n\n" . $content_type . "\n" . $expires . "\n" . '/' . $bucket_name . '/' . $file_name);
$private_key = json_decode(file_get_contents('xxx.json'))->private_key;
if(!openssl_sign( $to_sign, $signature, $private_key, 'sha256' ))
{
return false;
}
else
{
$signature = urlencode(base64_encode($signature));
}
error_log($to_sign);
return 'https://www.googleapis.com/upload/storage/v1/b/' . $bucket_name . '/o/' . urlencode($file_name) .
'?GoogleAccessId=' . 'xxx@xxx.iam.gserviceaccount.com' .
'&Expires=' . $expires .
'&Signature=' . $signature;
}
GCS has two APIs. The first, the XML API, uses domains like
storage.googleapis.com
. The second, the JSON API, uses domains likewww.googleapis.com
.You're using the JSON API, which is fine, but unfortunately it doesn't support signed URLs. Craft an upload URL using the XML API's format: https://cloud.google.com/storage/docs/xml-api/put-object-upload