我打算让我的应用程序的用户直接将文件上传到GCS,我正在使用带有签名网址的PUT request。我在python中的代码如下所示:
def get(self):
base_url = 'https://storage.googleapis.com/' + self.get_bucket_name()
expiration = utils.unix_time_secs(datetime.now() + timedelta(hours=1))
string_to_sign = ( 'PUT' + "\n" +
'' + "\n" +
'' + "\n" +
str(expiration) + "\n" +
'' + "\n" +
'my-bucket.appspot.com')
signed_string = app_identity.sign_blob(string_to_sign)[1]
signature = base64.b64encode(signed_string)
signature = urllib.quote(signature, safe='')
google_access_id = app_identity.get_service_account_name()
params = '?GoogleAccessId={0}&Expires={1}&Signature={2}'.format(
google_access_id, expiration, signature)
self.template_values['base_url'] = base_url
self.template_values['params'] = params
self.render('testA.html')
在客户端,我使用XMLHttpRequest将文件发送到GCS:
function gCloud (base_url, params, callback) {
var xhr = new XMLHttpRequest();
var file = document.querySelector('#fotos').files[0]
//Here goes an event listener (callback)
url = base_url + '/' + file.name + params
xhr.open("PUT", url, true);
xhr.setRequestHeader("Content-Type", file.type)
xhr.send(file)
}
问题在于,当我发送此请求时,我收到403 Forbidden Error,表示计算出的签名与我提供的签名不匹配。这可能是一个愚蠢的错误,但我不能让这个工作。可能是什么问题?
编辑:
我尝试使用google-cloud-python,并尝试了这个代码(有效):
def get(self):
bucket = storage.Client().get_bucket(self.get_bucket_name())
blob = bucket.blob('Yosemite.jpg')
expiration = utils.unix_time_secs(datetime.now() + timedelta(hours=1))
base_url = blob.generate_signed_url(expiration, "PUT", 'image/jpeg')
self.template_values['base_url'] = base_url
self.render('testA.html')
现在的问题是,由于“generate_signed_url”是它已经假设我知道对象的唯一路径来创建链接,像斑点斑点类的方法= bucket.blob(“Yosemite.jpg”),我没有。如果用户想要上传Space.jpg怎么办?而不是创建我的桶/ Space.jpg,Yosemite.jpg它将被覆盖,所以我将有Yosemite.jpg,如果我打开图像,它实际上将是Space.jpg。我该如何解决这个问题?
另外,如果我在'generate_signed_url'函数中不包含Content-Type ='image-jpeg',则签名将不匹配。如果用户上传了一个png怎么办?
答案 0 :(得分:2)
URL签名代码很棘手,而且很难调试。幸运的是,谷歌的谷歌云库有一个“generate_signed_url”功能,可以为你解决这个问题。我强烈建议您使用它而不是自己重写它。 Here's the documentation
即使您不想使用它,也可以查看implementation of the signing logic in Python。
现在,如果您想自己调试它,检查错误消息非常有用。它将包括服务器检查签名的字符串的完整副本。打印“string_to_sign”并查看它是否与从服务器返回的值匹配。如果没有,那就是你的问题。如果是,请继续进行实际签名。
查看您的代码,我的猜测是问题可能是您没有通过网址转发google_access_id,但我不确定。
答案 1 :(得分:1)
看起来您缺少规范资源的objectname部分。这是必需的,无法创建允许用户选择文件名的PUT签名URL,您必须在签署URL时指定它。
另请注意,SignatureDoesNotMatch错误的响应正文应该包含一些带有stringToSign的XML - 也就是服务器期望您签名的字符串。您可以将其与实际签名的字符串进行比较,以确定不匹配的来源。