在 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 编写一个函数)。 此数组的 ba​​se64 编码版本填充​​ 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,则签名或策略可能有误。查看响应文本。如果经常有帮助。


注意几点:


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 项目中效果很好。