5
votes

I'd like users of a site to be able to upload files to google cloud storage without using the web app servers resources so signed urls seem like the way to go.

When a user selects a file to upload jquery sends a GET request to django for a signed url. The url is generated using gsutil signurl command. Django then returns the signed url to the template and on submit a jquery PUT request is sent with the signed url.

However:

  • the PUT request fails with 'SignatureDoesNotMatch'.
  • GET requests for storage objects work fine using this method.

Are there required headers that must be sent with the PUT request?

gsutil command (assuming user selected file 'map.html')...

gsutil signurl -p notasecret -m PUT -d 10m /path/to/.p12 gs://bucket_name/map.html

jquery PUT code...

    $.ajax( {
  url: g_url,
  type: 'PUT',
  crossDomain: true,
  success: console.log('success'),
  error: function(XMLHttpRequest, textStatus, errorThrown){
    alert('status:' + XMLHttpRequest.status + ', status text: ' + XMLHttpRequest.statusText);
},
  data: file,
} );

g_url looks like...

https://storage.googleapis.com/bucket_name/map.html?GoogleAccessId=__retracted__&Expires=1408889274&Signature=rDJAZQG4MIyMupy0M8HJ17r8rkEJcAbYSWpcq084SdzRh%2BnZavTfuWl4Q%2F6ytkSkN2c2%2B4b4pPRF5eWOEOL1InRxlB5pEBedPFZPpgDrRvR9tFybtH%2BkesKLhIZ3WjJ0utzAwhl%2BgAlQY6ulvO0Djib20zcG5fkHOigpRf1xBUk%3D
1
I have a theory, but we'll have to investigate to make sure. The GCS XML API returns as part of its failure results the string it expected you to sign. That string must not match the upload that you're performing. What is the exact error message you get back?Brandon Yarbrough
Very interesting... the string to sign is GET 1408979265 /tell/map.html . Apparently my gsutil command is not generating a PUT request string.mhabiger
The opposite. The string to sign is the string that corresponds with the request being made, not the string that corresponds with the signature. I am guessing your AJAX call is a 'GET' for some reason. Can you check that?Brandon Yarbrough

1 Answers

1
votes

Turns out my problem was related to CORS. In order to get this to work on Django 1.6, I had to do the following:

With that PUT and DELETE requests started working. The only other issue I ran into was incompatible content-headers. So you my need to set your content-type in the signed url and before sending the request.

gsutil command would look like

gsutil signurl -p notasecret -m PUT -d 10m -c 'multipart/formdata; charset=UTF-8' /path/to/.p12 gs://bucket_name/map.html

add beforeSend to jquery request

beforeSend: function (request){
request.setRequestHeader("Content-Type", 'multipart/formdata; charset=UTF-8')
;},