在 Google Cloud Storage signed URL 中提供回调 URL
Provide a callback URL in Google Cloud Storage signed URL
使用 BlobStore 的 createUploadURL 功能上传到 GCS(Google 云存储)时,我可以提供一个回调以及 header 将被 POST 编辑的数据到回调 URL.
GCS 的 signed URL's
似乎没有办法做到这一点
我知道有 Object Change Notification,但这不允许用户在 POST 的 header 中提供上传特定信息,而 createUploadURL的回调。
我的感觉是,如果 createUploadURL 可以做到,那么一定有办法用签名的 URL 做到这一点,但我找不到任何关于它的文档。我想知道是否有人知道 createUploadURL 是如何实现回调调用行为的。
PS:我正试图摆脱 createUploadURL 因为它创建了 __BlobInfo__
实体,对于我的特定用例我不需要,不知何故似乎是不可磨灭的并且正在浪费存储空间 space.
更新: 成功了!方法如下:
简答:PUT, but can be done with POST
无法完成
长答案:
如果您查看 signed-URL 页面,在 HTTP_Verb 之前,在 Description 下,有一个微妙的注释,即此页面仅相关GET、HEAD、PUT 和 DELETE,但 POST 是一个完全不同的游戏。我错过了这个,但事实证明它非常重要。
有一整页HTTP Headers没有列出一个可以与POST一起使用的重要header; header 是 success_action_redirect,正如 voscausa 正确回答的那样。
在POST页面Google"strongly recommends"使用PUT,除非处理表单数据。然而,POST 有一些 PUT 没有的好特性。他们可能担心 POST 给了我们太多的绳子让我们上吊。
但我认为放弃 createUploadURL 并编写您自己的代码以重定向到回调是完全值得的。方法如下:
代码:
如果您在 Python 工作,voscausa 的 code 非常有帮助。
我正在使用 apejs 在 Java 应用程序中编写 javascript,所以我的代码如下所示:
var exp = new Date()
exp.setTime(exp.getTime() + 1000 * 60 * 100); //100 minutes
json['GoogleAccessId'] = String(appIdentity.getServiceAccountName())
json['key'] = keyGenerator()
json['bucket'] = bucket
json['Expires'] = exp.toISOString();
json['success_action_redirect'] = "https://" + request.getServerName() + "/test2/";
json['uri'] = 'https://' + bucket + '.storage.googleapis.com/';
var policy = {'expiration': json.Expires
, 'conditions': [
["starts-with", "$key", json.key],
{'Expires': json.Expires},
{'bucket': json.bucket},
{"success_action_redirect": json.success_action_redirect}
]
};
var plain = StringToBytes(JSON.stringify(policy))
json['policy'] = String(Base64.encodeBase64String(plain))
var result = appIdentity.signForApp(Base64.encodeBase64(plain, false));
json['signature'] = String(Base64.encodeBase64String(result.getSignature()))
上面的代码首先提供了相关字段。
然后创建一个策略 object。然后它将 object 字符串化并将其转换为字节数组(您可以在 Java 中使用 .getBytes。我不得不为 javascript 编写一个函数)。
此数组的 base64 编码版本填充 policy 字段。
然后使用 appidentity 包对其进行签名。最后将签名进行base64编码,大功告成
在客户端,json object 的所有成员都将添加到表单中,除了作为表单地址的 uri。
var formData = new FormData(document.forms.namedItem('upload'));
var blob = new Blob([thedata], {type: 'application/json'})
var keys = ['GoogleAccessId', 'key', 'bucket', 'Expires', 'success_action_redirect', 'policy', 'signature']
for(field in keys)
formData.append(keys[field], url[keys[field]])
formData.append('file', blob)
var rest = new XMLHttpRequest();
rest.open('POST', url.uri)
rest.onload = callback_function
rest.send(formData)
如果您不提供重定向,响应状态将为 204,表示成功。但是,如果您进行重定向,状态将为 200。如果您收到 403 或 400,则签名或策略可能有误。查看响应文本。如果经常有帮助。
注意几点:
- POST 和 PUT 都有一个签名字段,但它们的含义略有不同。在 POST 的情况下,这是 策略 的签名。
- PUT 有一个包含密钥(object 名称)的 baseurl,但用于 POST 的 URL 可能只包含存储桶名称
- PUT 要求从 UNIX 纪元起以秒为单位过期,但 POST 希望它作为 ISO 字符串。
- PUT 签名应该 URL 编码(Java:用 URLEncoder.encode 调用包装它)。但是对于POST,Base64编码就足够了。
- 通过扩展,对于 POST 执行 Base64.encodeBase64String(result.getSignature()),并且不要使用 Base64.encodeBase64URLSafeString 函数
- 您不能通过 POST 传递额外的 header;仅允许 POST page 中列出的内容。
- 如果您为 success_action_redirect 提供 URL,它将收到带有 key、bucket[=107= 的 GET ] 和 eTag.
- 使用 POST 的另一个好处是您可以提供大小限制。但是,使用 PUT,如果文件超出了您的大小限制,您只能在完全上传后将其删除,即使它是 multiple-tera-bytes.
createUpload有什么问题URL?
以上方法是手动createUploadURL。
但是:
- 您不会得到那些创建许多索引并且不可磨灭的
__BlobInfo__
objects。这让我很恼火,因为它浪费了很多space(这让我想起了一个单独的问题:issue 4231。请给它一个星)
- 您可以提供自己的 object 名称,这有助于在您的存储桶中创建文件夹。
- 您可以为每个link提供不同的到期日期。
对于极少数javascriptapp-engineers:
function StringToBytes(sz) {
map = function(x) {return x.charCodeAt(0)}
return sz.split('').map(map)
}
当您使用 GCS post 对象时,您可以在策略文档中包含 succes_action_redirect。
文档在这里:文档:https://cloud.google.com/storage/docs/xml-api/post-object
Python 此处示例:https://github.com/voscausa/appengine-gcs-upload
回调结果示例:
def ok(self):
""" GCS upload success callback """
logging.debug('GCS upload result : %s' % self.request.query_string)
bucket = self.request.get('bucket', default_value='')
key = self.request.get('key', default_value='')
key_parts = key.rsplit('/', 1)
folder = key_parts[0] if len(key_parts) > 1 else None
我正在使用的解决方案是打开 Object Changed Notifications。任何时候添加一个对象,一个 Post 被发送到一个 URL - 在我的例子中 - 我项目中的一个 servlet。
在 doPost()
中,我将所有反对的信息添加到 GCS,然后从那里,我可以做任何事情。
这在我的 App Engine 项目中效果很好。
使用 BlobStore 的 createUploadURL 功能上传到 GCS(Google 云存储)时,我可以提供一个回调以及 header 将被 POST 编辑的数据到回调 URL.
GCS 的 signed URL's
似乎没有办法做到这一点我知道有 Object Change Notification,但这不允许用户在 POST 的 header 中提供上传特定信息,而 createUploadURL的回调。
我的感觉是,如果 createUploadURL 可以做到,那么一定有办法用签名的 URL 做到这一点,但我找不到任何关于它的文档。我想知道是否有人知道 createUploadURL 是如何实现回调调用行为的。
PS:我正试图摆脱 createUploadURL 因为它创建了 __BlobInfo__
实体,对于我的特定用例我不需要,不知何故似乎是不可磨灭的并且正在浪费存储空间 space.
更新: 成功了!方法如下:
简答:PUT, but can be done with POST
无法完成长答案:
如果您查看 signed-URL 页面,在 HTTP_Verb 之前,在 Description 下,有一个微妙的注释,即此页面仅相关GET、HEAD、PUT 和 DELETE,但 POST 是一个完全不同的游戏。我错过了这个,但事实证明它非常重要。
有一整页HTTP Headers没有列出一个可以与POST一起使用的重要header; header 是 success_action_redirect,正如 voscausa 正确回答的那样。
在POST页面Google"strongly recommends"使用PUT,除非处理表单数据。然而,POST 有一些 PUT 没有的好特性。他们可能担心 POST 给了我们太多的绳子让我们上吊。
但我认为放弃 createUploadURL 并编写您自己的代码以重定向到回调是完全值得的。方法如下:
代码:
如果您在 Python 工作,voscausa 的 code 非常有帮助。
我正在使用 apejs 在 Java 应用程序中编写 javascript,所以我的代码如下所示:
var exp = new Date()
exp.setTime(exp.getTime() + 1000 * 60 * 100); //100 minutes
json['GoogleAccessId'] = String(appIdentity.getServiceAccountName())
json['key'] = keyGenerator()
json['bucket'] = bucket
json['Expires'] = exp.toISOString();
json['success_action_redirect'] = "https://" + request.getServerName() + "/test2/";
json['uri'] = 'https://' + bucket + '.storage.googleapis.com/';
var policy = {'expiration': json.Expires
, 'conditions': [
["starts-with", "$key", json.key],
{'Expires': json.Expires},
{'bucket': json.bucket},
{"success_action_redirect": json.success_action_redirect}
]
};
var plain = StringToBytes(JSON.stringify(policy))
json['policy'] = String(Base64.encodeBase64String(plain))
var result = appIdentity.signForApp(Base64.encodeBase64(plain, false));
json['signature'] = String(Base64.encodeBase64String(result.getSignature()))
上面的代码首先提供了相关字段。 然后创建一个策略 object。然后它将 object 字符串化并将其转换为字节数组(您可以在 Java 中使用 .getBytes。我不得不为 javascript 编写一个函数)。 此数组的 base64 编码版本填充 policy 字段。 然后使用 appidentity 包对其进行签名。最后将签名进行base64编码,大功告成
在客户端,json object 的所有成员都将添加到表单中,除了作为表单地址的 uri。
var formData = new FormData(document.forms.namedItem('upload'));
var blob = new Blob([thedata], {type: 'application/json'})
var keys = ['GoogleAccessId', 'key', 'bucket', 'Expires', 'success_action_redirect', 'policy', 'signature']
for(field in keys)
formData.append(keys[field], url[keys[field]])
formData.append('file', blob)
var rest = new XMLHttpRequest();
rest.open('POST', url.uri)
rest.onload = callback_function
rest.send(formData)
如果您不提供重定向,响应状态将为 204,表示成功。但是,如果您进行重定向,状态将为 200。如果您收到 403 或 400,则签名或策略可能有误。查看响应文本。如果经常有帮助。
注意几点:
- POST 和 PUT 都有一个签名字段,但它们的含义略有不同。在 POST 的情况下,这是 策略 的签名。
- PUT 有一个包含密钥(object 名称)的 baseurl,但用于 POST 的 URL 可能只包含存储桶名称
- PUT 要求从 UNIX 纪元起以秒为单位过期,但 POST 希望它作为 ISO 字符串。
- PUT 签名应该 URL 编码(Java:用 URLEncoder.encode 调用包装它)。但是对于POST,Base64编码就足够了。
- 通过扩展,对于 POST 执行 Base64.encodeBase64String(result.getSignature()),并且不要使用 Base64.encodeBase64URLSafeString 函数
- 您不能通过 POST 传递额外的 header;仅允许 POST page 中列出的内容。
- 如果您为 success_action_redirect 提供 URL,它将收到带有 key、bucket[=107= 的 GET ] 和 eTag.
- 使用 POST 的另一个好处是您可以提供大小限制。但是,使用 PUT,如果文件超出了您的大小限制,您只能在完全上传后将其删除,即使它是 multiple-tera-bytes.
createUpload有什么问题URL?
以上方法是手动createUploadURL。 但是:
- 您不会得到那些创建许多索引并且不可磨灭的
__BlobInfo__
objects。这让我很恼火,因为它浪费了很多space(这让我想起了一个单独的问题:issue 4231。请给它一个星) - 您可以提供自己的 object 名称,这有助于在您的存储桶中创建文件夹。
- 您可以为每个link提供不同的到期日期。
对于极少数javascriptapp-engineers:
function StringToBytes(sz) {
map = function(x) {return x.charCodeAt(0)}
return sz.split('').map(map)
}
当您使用 GCS post 对象时,您可以在策略文档中包含 succes_action_redirect。
文档在这里:文档:https://cloud.google.com/storage/docs/xml-api/post-object
Python 此处示例:https://github.com/voscausa/appengine-gcs-upload
回调结果示例:
def ok(self):
""" GCS upload success callback """
logging.debug('GCS upload result : %s' % self.request.query_string)
bucket = self.request.get('bucket', default_value='')
key = self.request.get('key', default_value='')
key_parts = key.rsplit('/', 1)
folder = key_parts[0] if len(key_parts) > 1 else None
我正在使用的解决方案是打开 Object Changed Notifications。任何时候添加一个对象,一个 Post 被发送到一个 URL - 在我的例子中 - 我项目中的一个 servlet。
在 doPost()
中,我将所有反对的信息添加到 GCS,然后从那里,我可以做任何事情。
这在我的 App Engine 项目中效果很好。