华为云对象存储表单上传

每个华为云对象存储的用户,都会用到上传。大多数都是在服务器通过 SDK 上传到华为云对象存储。然而很多应用对在浏览器上传文件到 华为云对象存储 的需求很强烈,采用的做法是用户在浏览器上传到应用服务器,然后应用服务器再把文件上传到 华为云对象存储。 这种方法有三个缺点:
第一:上传慢。先上传到应用服务器,再上传到 华为云对象存储,网络传送多了一倍。如果数据直传到华为云对象存储,不走应用服务器,速度将大大提升。
第二:费用高。由于 华为云对象存储 上传流量是免费的。如果数据直传到 华为云对象存储,不走应用服务器,将节省应用服务器的带宽。
第三:开发维护代价高。应用服务器需要开发相关接收并转发数据到 华为云对象存储 的功能。后期还存在扩展维护等开销。

华为云对象存储表单直传
表单直传就是在浏览器通过 JaveScript 生成表单直接上传到 华为云对象存储。包括用于鉴权的 Signature,文件名字段 key 等连同文件一起封装成表单直接上传到 华为云对象存储。 表单内容参考如下:
 
------WebKitFormBoundaryVDu6DqT79PvZYSgZ
Content-Disposition: form-data; name="AWSAccessKeyId"                   //access key
 
9cea0fc32fa742fe811e75bd26112ecb
------WebKitFormBoundaryVDu6DqT79PvZYSgZ
Content-Disposition: form-data; name="policy"
 
eyJjb25kaXRpb25zIjogW3siYnVja2V0IjogImZvcm1wb3N0In0sIFsic3RhcnRzLXdpdGgiLCAiJGtleSIsICJ1cGxvYWQvIl1dLCAiZXhwaXJhdGlvbiI6ICIyMDE3LTEyLTAxVDAwOjAwOjAwWiJ9Cgo=
------WebKitFormBoundaryVDu6DqT79PvZYSgZ
Content-Disposition: form-data; name="signature"                       // signature
 
MC56JWFf58SSV23/pPss8aM4XXM=
------WebKitFormBoundaryVDu6DqT79PvZYSgZ
Content-Disposition: form-data; name="key"                             // 文件名(key)
 
upload/js2017
------WebKitFormBoundaryVDu6DqT79PvZYSgZ
Content-Disposition: form-data; name="file"; filename="js2017"
Content-Type: application/octet-stream
 
hello js                                                               // 文件内容:hello js
------WebKitFormBoundaryVDu6DqT79PvZYSgZ
Content-Disposition: form-data; name="submit"
 
Upload
------WebKitFormBoundaryVDu6DqT79PvZYSgZ--
policy解析
base64 -D <<< 'eyJjb25kaXRpb25zIjogW3siYnVja2V0IjogImZvcm1wb3N0In0sIFsic3RhcnRzLXdpdGgiLCAiJGtleSIsICJ1cGxvYWQvIl1dLCAiZXhwaXJhdGlvbiI6ICIyMDE3LTEyLTAxVDAwOjAwOjAwWiJ9Cgo='
{"conditions": [
    {"bucket": "formpost"},                                     # 表示上传的Bucket为formpost
    ["starts-with", "$key", "upload/"]                       # 上传的文件名需以upload/开头
],
"expiration": "2017-12-01T00:00:00Z"                 # 此policy在2017.12.01之前有效
}

表单上传鉴权
文件上传需要通过鉴权。表单上传鉴权(singnature)的生成可以在浏览器直接填写 access key 和 secret key 生成。但是这样会暴露 secret key,带来安全隐患。 我们这里采用向后台申请获得 access key、policy 和 signature 的方式来完成上传。示意图如下: 表单上传鉴权
 
应用后台开发示例
#-*- coding:utf-8 -*-
import BaseHTTPServer
import base64, json
import hmac, hashlib
import sys,os
 
host = "华为云对象存储test-corp.sankuai.com"
bucket = "formpost"
upload_dir = "upload"
 
access_key = "**"
secret_key = "**"
 
def formpost_token():
    policy_dict = {
        "expiration": "2017-12-01T00:00:00Z",
        "conditions": [
            {"bucket": bucket},
            ["starts-with", "$key", upload_dir],       // 表示上传文件名只能以upload/开头
        ]
    }
    policy_document = json.dumps(policy_dict)
    policy = base64.b64encode(policy_document)
    signature = base64.b64encode(hmac.new(secret_key, policy, hashlib.sha1).digest())
 
    formFields = [
        ('key', 'upload/${filename}'),
        ('AWSAccessKeyId', access_key),
        ('signature', signature),
        ('policy', policy)
    ]
 
    token_dict = {}
    token_dict['accessid'] = access_key
    token_dict['host'] = host
    token_dict['policy'] = policy
    token_dict['signature'] = signature
    token_dict['dir'] = upload_dir
 
    result = json.dumps(token_dict)
    return result
 
class RequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
    def do_GET(self):
        try:
            token = formpost_token()
            self.send_content(token)
        except Exception as msg:
            self.handle_error(msg)
 
    def handle_error(self, msg):
        self.send_content(msg, 404)
 
    def send_content(self, content, status=200):
        self.send_response(status)
        self.send_header("Content-type", "text/html")
        self.send_header("Origin", "*")
        self.send_header("Content-Length", str(len(content)))
 
        self.send_header("Access-Control-Allow-Headers", "content-type,x-auth-token")
        self.send_header("Access-Control-Allow-Methods", "GET,POST,PUT,DELETE,OPTIONS")
        self.send_header("Access-Control-Allow-Origin", "*")
        self.send_header("Access-Control-Expose-Headers", "Cache-Control,Content-Type,Expires,Last-Modified,ETag,X-Timestamp,X-华为云对象存储-Trace-id,X-Container-Object-Count,X-Container-Bytes-Used,X-Account-Container-Count,X-Account-Object-Count,X-Account-Bytes-Used,X-Static-Large-Object,X-Container-Read,X-Container-Write,X-Auth-Token")
 
        self.end_headers()
        self.wfile.write(content)
 
if __name__ == '__main__':
    serverAddress = ('', 8000)
    server = BaseHTTPServer.HTTPServer(serverAddress, RequestHandler)
    server.serve_forever()

表单上传操作
创建应用后台程序并写入示例代码,然后启动应用后台程序
点击这里,打开表单上传 JS 页面
在 Application URL 填写应用后台地址(例子中的地址为http://10.4.240.234:8000),然后点击 Set Application URL,会在表单域示例产生 AWSAccessKeyId、policy 和 signature 三项
在 Post URL 填写上传地址,示例中上传的桶名为 formpost,然后点击 Set URL
如果想修改默认文件名,可以删除表单域 key。然后新添加表单域 Name:key,Value 字段需以 upload/ 作为前缀(匹配 policy 里面的 key)
添加上传文件,示例添加的文件名为js2017
点击 Upload 按钮完成上传

标签

发表评论