在 Google Cloud Storage 签名 URL 中提供回调 URL

2024-01-07

使用 BlobStore 上传到 GCS(Google Cloud Storage)时创建上传URL https://cloud.google.com/appengine/docs/java/javadoc/com/google/appengine/api/blobstore/BlobstoreService#createUploadUrl-java.lang.String-函数中,我可以提供一个回调以及将被 POST 到回调 URL 的标头数据。

GCS 似乎没有办法做到这一点签名的 URL https://cloud.google.com/storage/docs/access-control#Signed-URLs

我知道有对象变更通知 https://cloud.google.com/storage/docs/object-change-notification但这不允许用户在 POST 标头中提供上传特定信息,而 createUploadURL 的回调可以采用这种方式。

我的感觉是,如果 createUploadURL 可以做到这一点,那么一定有一种方法可以使用签名 URL 来做到这一点,但我找不到任何相关文档。我想知道是否有人知道 createUploadURL 如何实现回调调用行为。


PS:我正在尝试放弃 createUploadURL 因为__BlobInfo__它创建的实体,对于我的特定用例我不需要,并且不知何故似乎是不可消除的并且浪费存储空间。


Update:有效!具体方法如下:

简短回答:这是不能用PUT https://cloud.google.com/storage/docs/access-control?hl=en#Signed-URLs,但可以用POST https://cloud.google.com/storage/docs/xml-api/post-object

长答案:

如果你看一下签名 URL https://cloud.google.com/storage/docs/access-control?hl=en#Signed-URLs页,在 HTTP_Verb 前面,在描述,有一个微妙的说明,这个页面只与 GET、HEAD、PUT 和 DELETE 相关,但 POST 是一个完全不同的游戏。我错过了这一点,但事实证明它非常重要。

有一整页HTTP 标头 https://cloud.google.com/storage/docs/reference-headers没有列出可与 POST 一起使用的重要标头;该标头是 success_action_redirect,正如 voscausa 正确回答的那样。

在 POST 页面中,Google“强烈建议”使用 PUT,除非处理表单数据。然而,POST 有一些 PUT 所没有的好功能。他们可能担心 POST 给我们带来了太多的束缚。

但我想说,完全值得放弃 createUploadURL,并编写自己的代码来重定向到回调。具体方法如下:


Code:

如果您正在使用 Python voscausacode https://github.com/voscausa/appengine-gcs-upload/blob/master/gcs_upload.py非常有帮助。

我在用着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()))

上面的代码首先提供了相关字段。 然后创建一个策略对象。然后它对对象进行字符串化并将其转换为字节数组(您可以在Java中使用.getBytes。我必须为javascript编写一个函数)。 该数组的 Base64 编码版本填充policy场地。 然后使用附身性 https://cloud.google.com/appengine/docs/java/appidentity/包裹。最后签名是base64编码的,我们就完成了。

在客户端,json对象的所有成员都会被添加到Form中,除了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,这是签名policy.
  • PUT 有一个包含 key(对象名称)的 baseurl,但用于 POST 的 URL 可能只包含存储桶名称
  • PUT 要求过期时间为 UNIX 纪元的秒数​​,但 POST 希望它为 ISO 字符串。
  • PUT 签名应该是 URL 编码的(Java:通过使用 URLEncoder.encode 调用包装它)。但对于 POST,Base64 编码就足够了。
  • 通过扩展,对于 POST 执行 Base64.encodeBase64String(result.getSignature()),并且不要使用 Base64.encodeBase64URLSafeString 函数
  • 您不能通过 POST 传递额外的标头;仅那些列在发布页面 https://cloud.google.com/storage/docs/xml-api/post-object被允许。
  • 如果您为 success_action_redirect 提供 URL,它将收到带有以下内容的 GETkey, bucket and eTag.
  • 使用 POST 的另一个好处是您可以提供大小限制。然而,使用 PUT 时,如果文件违反了大小限制,您只能在完全上传后将其删除,即使它有多个 TB 字节。

createUploadURL 有什么问题吗?

上面的方法是手动的创建上传URL. But:

  • 你不明白这些__BlobInfo__创建许多索引并且不可删除的对象。这让我很恼火,因为它浪费了很多空间(这让我想起了一个单独的问题:问题 4231 https://code.google.com/p/googleappengine/issues/detail?id=4231。请去给它一颗星)
  • 您可以提供自己的对象名称,这有助于在存储桶中创建文件夹。
  • 您可以为每个链接提供不同的到期日期。

对于极少数 javascript 应用工程师:

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 https://cloud.google.com/storage/docs/xml-api/post-object
Python 示例在这里:https://github.com/voscausa/appengine-gcs-upload 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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 Google Cloud Storage 签名 URL 中提供回调 URL 的相关文章

随机推荐