I am trying to upload audio files on the Client-Side directly to my Google
Cloud Storage bucket, for the purpose of avoiding a server-side upload
(which has file size limits).
My Issue: I am getting a 403 SignatureDoesNotMatch error on upload.
Here is the error from the response:
1. <Error>
1. <Code>SignatureDoesNotMatch</Code>
2. <Message>The request signature we calculated does not match the
signature you provided. Check your Google secret key and signing method.
</Message>
3. <StringToSign>PUT audio/mp3 1511112552
/bucketname/pathtofile/019%20-%20top%20cntndr%20V1.mp3</StringToSign>
4. </Error>
I have created a signed url. It looks like this:
https://storage.googleapis.com/google-testbucket/testdata.txt?GoogleAccessId=
[email protected]&Expires=1331155464&Signature=BCl
z9e4UA2MRRDX62TPd8sNpUCxVsqUDG3YGPWvPcwN%2BmWBPqwgUYcOSszCPlgWREeF7oPGowkeKk
7J4WApzkzxERdOQmAdrvshKSzUHg8Jqp1lw9tbiJfE2ExdOOIoJVmGLoDeAGnfzCd4fTsWcLbal9
sFpqXsQI8IQi1493mw%3D
The signed url is built following the guidlines found in the Google Docs
here
https://cloud.google.com/storage/docs/access-control/create-signed-urls-program
However, the client-side javascript portion of handling this signed url is
very unclear in the documentation.
Here is my python code to create and return the signed url.
GOOGLE_SERVICE_CREDENTIALS = 'google-service-credentials.json'
def get_signed_url(request):
filename = request.GET.get('filename')
expiration = request.GET.get('expiration')
type = request.GET.get('type')
signed_url = CloudStorageSignedURL(
method='PUT',
file_name=filename,
expiration_m=expiration,
content_type=type
)
signed_url = signed_url.sign_url()
return JsonResponse({ 'signed_url': signed_url })
class CloudStorageSignedURL(object):
def __init__(self, method, file_name, expiration_m, content_type):
self.HTTP_method = method
self.content_type = 'content-type: ' + content_type
self.expiration = int(expiration_m)
self.file_name = file_name
def sign_url(self):
expiration_dt = datetime.utcnow() + timedelta(minutes=self.
expiration)
expiration = int(time.mktime( expiration_dt.timetuple() ))
bucket_path = '/' + settings.CLOUD_STORAGE_BUCKET + '/dev/tests/' +
self.file_name
signature_string = self.HTTP_method + '\n' + '\n' + self.content_type
+ "\n" + str(expiration) + '\n' + bucket_path
print(signature_string)
creds = ServiceAccountCredentials.from_json_keyfile_name(
GOOGLE_SERVICE_CREDENTIALS)
client_email = creds.service_account_email
signature = creds.sign_blob(signature_string)[1]
encoded_signature = base64.urlsafe_b64encode(signature).decode(
'utf-8')
base_url = settings.CLOUD_STORAGE_ROOT + 'dev/tests/' + self.
file_name
return base_url + '?GoogleAccessId=' + client_email + '&Expires=' +
str(expiration) + '&Signature=' + encoded_signature
*Client-side Javascript to upload the file*
import $ from 'jquery';
import axios from 'axios';
$("document").ready( () => {
console.log('window loaded');
$("#id_audio_file").change(function() {
const file = this.files[0]
const url = window.location.href.replace('submit/', 'upload/');
$.get(url + `?filename=${file.name}&expiration=10&type=${file.type}`, (
data) => {
upload(data.signed_url, file);
})
});
});
function upload(url, file) {
const config = {
headers: {
'Content-Type': file.type,
}
}
axios.put(url, file, config)
.then(function (res) {
console.log(res);
})
.catch(function (err) {
console.log(err);
});
}
I really feel like I covered all the bases here, but I am obviously missing
something minute. Any help would be greatly appreciated!
--
You received this message because you are subscribed to the Google Groups
"Google App Engine" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/google-appengine.
To view this discussion on the web visit
https://groups.google.com/d/msgid/google-appengine/41387516-152e-4a24-83b0-4013289e24e0%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.