在Google云端存储签名网址中提供回调网址

use*_*503 8 upload google-app-engine callback blobstore google-cloud-storage

当使用BlobStore的createUploadURL函数上传到GCS(Google云端存储)时,我可以提供回调以及将被发布到回调URL的标题数据.

使用GCS的签名URL似乎没有办法做到这一点

我知道有对象更改通知但不允许用户在POST的标题中提供上传特定信息,这可以通过createUploadURL的回调来实现.

我的感觉是,如果createUploadURL能做到这一点,必须有办法用标识的URL的做到这一点,但我找不到它的任何文件.我想知道是否有人可能知道createUploadURL如何实现该回调调用行为.


PS:我正在尝试离开createUploadURL因为__BlobInfo__它创建的实体,这对于我不需要的特定用例,并且似乎是不可磨灭的并且浪费存储空间.


更新:它工作了!方法如下:

简答:PUT无法完成,但可以通过POST完成

答案很长:

如果你看看签署-URL页面,在HTTP_Verb面前,在说明,有一个微妙的注意,该页面只是相关的GET,HEAD,PUT和DELETE,但POST是一个完全不同的游戏.我错过了这个,但事实证明这非常重要.

有一整页HTTP标头没有列出可以与POST一起使用的重要标头; 该标题是success_action_redirect,正如voscausa正确回答的那样.

在POST页面中,Google"强烈建议"使用PUT,除非处理表单数据.但是,POST有一些很好的功能,PUT没有.他们可能会担心POST给我们带来了太多的字符串.

但我要说完全值得删除createUploadURL,并编写自己的代码来重定向到回调.方法如下:


码:

如果您正在使用Python voscausa的代码非常有帮助.

我正在使用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()))
Run Code Online (Sandbox Code Playgroud)

上面的代码首先提供相关字段.然后创建一个策略对象.然后它将字符串化为对象并将其转换为字节数组(您可以在Java中使用.getBytes.我必须为javascript编写一个函数).此数组的base64编码版本填充策略字段.然后使用appidentity包签名.最后签名是base64编码的,我们完成了.

在客户端,json对象的所有成员将被添加到表单中,除了作为表单地址的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)
Run Code Online (Sandbox Code Playgroud)

如果您未提供重定向,则响应状态将为204以获得成功.但是如果你做重定向,状态将是200.如果你得到403或400有关签名或政策可能是错误的.看看responseText.如果经常有帮助.


有几点需要注意:

  • POST和PUT都有一个签名字段,但这些意味着略有不同.在POST的情况下,这是该策略的签名.
  • PUT有一个包含密钥(对象名称)的baseurl,但用于POST的URL可能只包含存储桶名称
  • PUT要求从UNIX纪元开始到期为止,但POST要求它作为ISO字符串.
  • PUT签名应该是URL编码的(Java:通过URLEncoder.encode调用包装它).但对于POST,Base64编码就足够了.
  • 通过扩展,对于POST执行Base64.encodeBase64String(result.getSignature()),并且不要使用Base64.encodeBase64URLSafeString函数
  • 你无法通过POST传递额外的标题; 只允许POST页面中列出的那些.
  • 如果您提供success_action_redirect的URL,它将收到带有密钥,存储桶eTag的GET .
  • 使用POST的另一个好处是您可以提供大小限制.然而随着PUT,如果一个文件违反您的大小限制,只能将其删除后,这是完全上传,哪怕是多万亿字节.

createUploadURL有什么问题?

上面的方法是手动createUploadURL.但:

  • 您没有获得那些__BlobInfo__创建许多索引并且不可磨灭的对象.这让我感到恼火,因为它浪费了很多空间(这让我想起了一个单独的问题:问题4231.请给它一个明星)
  • 您可以提供自己的对象名称,这有助于在存储桶中创建文件夹.
  • 您可以为每个链接提供不同的到期日期.

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

function StringToBytes(sz) {
  map = function(x) {return x.charCodeAt(0)}
  return sz.split('').map(map)
}
Run Code Online (Sandbox Code Playgroud)

vos*_*usa 2

当您使用 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
Run Code Online (Sandbox Code Playgroud)