文档首页> 对象存储服务 OBS> 最佳实践> Web端通过PostObject接口直传OBS
更新时间:2024-03-25 GMT+08:00

Web端通过PostObject接口直传OBS

背景信息

常见的Web端上传方法是用户通过浏览器上传文件至应用服务器,再由应用服务器上传至OBS,数据需要在应用服务器中转,传输效率较低,且多任务同时上传时应用服务器压力大。

本文介绍一种在Web端利用PostObject接口直传文件至OBS的方法,即使用表单上传方式上传文件至OBS。如图1所示,该方案省去了应用服务器这一步骤,提高了传输效率,不会对服务器产生压力,且服务端签名后直传可以保证传输的安全性。

图1 Web端PostObject直传流程图

前提条件

已创建桶。具体操作请参见创建桶

操作步骤

配置分为两大步:配置跨域资源共享和使用表单上传。

第一步:配置跨域资源共享

在通常的网页请求中,由于同源安全策略SOP的存在,不同域之间的网站脚本和内容是无法进行交互的。

跨域资源共享CORS是一种网络浏览器的规范机制,定义了一个域中加载的客户端Web应用程序与另一个域中的资源交互的方式。OBS支持CORS规范,允许跨域请求访问OBS中的资源。

  1. 在OBS管理控制台左侧导航栏选择“对象存储”
  2. 在桶列表单击待操作的桶,进入对象页面。
  3. 在左侧导航栏,单击“访问权限控制 > CORS规则”。
  4. 单击“创建”,系统弹出“创建CORS规则”对话框,如图2所示。

    一个桶最多可设置100条CORS规则。

    图2 创建CORS规则

  5. 在“CORS规则”中配置“允许的来源”、“允许的方法”、“允许的头域”、“补充头域”和“缓存时间”。

    如果该OBS桶同时开启了CDN加速,CDN需配置HTTP header,详见HTTP header配置

    表1 CORS规则

    参数

    说明

    配置建议

    允许的来源

    必选参数,指定允许的跨域请求的来源,即允许来自该域名下的请求访问该桶。

    允许多条匹配规则,以回车换行为间隔。每个匹配规则允许使用最多一个“*”通配符。例如:

    http://rds.example.com
    https://*.vbs.example.com

    *

    允许的方法

    必选参数,指定允许的跨域请求方法,即桶和对象的几种操作类型。包括:Get、Post、Put、Delete、Head。

    全选

    允许的头域

    可选参数,指定允许的跨域请求的头域。只有匹配上允许的头域中的配置,才被视为是合法的CORS请求。

    允许的头域可设置多个,多个头域之间换行隔开,每行最多可填写一个*符号,不支持&、:、<、空格以及中文字符。

    *

    补充头域

    可选参数,指CORS响应中带的补充头域,给客户端提供额外的信息。

    补充头域可设置多个,多个头域之间换行隔开,不支持*、&、:、<、空格以及中文字符。

    • ETag
    • x-obs-request-id
    • x-obs-api
    • Content-Type
    • Content-Length
    • Cache-Control
    • Content-Disposition
    • Content-Encoding
    • Content-Language
    • Expires
    • x-obs-id-2
    • x-reserved-indicator
    • x-obs-version-id
    • x-obs-copy-source-version-id
    • x-obs-storage-class
    • x-obs-delete-marker
    • x-obs-expiration
    • x-obs-website-redirect-location
    • x-obs-restore
    • x-obs-version
    • x-obs-object-type
    • x-obs-next-append-position

    缓存时间

    必选参数,请求来源的客户端可以缓存的CORS响应时间,以秒为单位,默认为100秒。

    根据实际业务设置。

  6. 单击“确定”。

    “CORS规则”页签显示“创建CORS规则成功”提示创建桶的CORS配置成功。CORS配置会在两分钟内生效。

    CORS配置成功后,便仅允许跨域请求来源的地址通过允许的方法访问OBS的桶。例如:为桶“testbucket”允许的来源配置为“https://www.example.com”,允许的方法配置为“GET”,允许的头域和补充的头域配置为“*”,缓存时间设置为“100”,则OBS仅允许来源为“https://www.example.com”的“GET”请求访问桶“testbucket”,且不限制该请求的头域,请求来源的客户端可缓存的该CORS请求的响应时间为100秒。

第二步:使用表单上传

以BrowserJS为例,演示如何直接使用SDK计算签名。

基于表单上传是使用HTML表单形式上传对象到指定桶中,对象最大不能超过5GB。

您可以通过ObsClient.createPostSignatureSync生成基于表单上传的请求参数。使用BrowserJS代码模拟表单上传的完整代码示例,可单击此处下载:post-object-sample。您也可以通过如下步骤进行表单上传:

  1. 使用ObsClient.createPostSignatureSync生成用于鉴权的请求参数。

    使用SDK生成用于鉴权的请求参数包括两个:
    • Policy:对应表单中policy字段。
    • Signature:对应表单中的signature字段。
    代码示例如下:
    // 创建ObsClient实例
    var obsClient = new ObsClient({
        // 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全;本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量AccessKeyID和SecretAccessKey。
        // 您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/intl/zh-cn/usermanual-ca/ca_01_0003.html
        access_key_id: process.env.AccessKeyID,
        secret_access_key: process.env.SecretAccessKey,
        server : 'https://your-endpoint',
        signature : 'obs'
    });
    
    // 设置表单参数
    var formParams = {
                  // 设置对象访问权限为公共读
                  'x-obs-acl': obsClient.enums.AclPublicRead, 
                  // 设置对象MIME类型
                  'content-type': 'text/plain'           
    };
    
    // 设置表单上传请求有效期,单位:秒
    var expires = 3600;
    
    var res = obsClient.createPostSignatureSync({Expires:expires, FormParams: formParams});
    
    // 获取表单上传请求参数
    console.log('\t' + res.Policy);
    console.log('\t' + res.Signature);

  2. 准备表单HTML页面。

    表单HTML代码示例如下:
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    </head>
    <body>
    
    <form action="http://bucketname.your-endpoint/" method="post" enctype="multipart/form-data">
    Object key
    <!-- 对象名 -->
    <input type="text" name="key" value="objectname" />
    <p>
    ACL
    <!-- 对象ACL权限 -->
    <input type="text" name="x-obs-acl" value="public-read" />
    <p>
    Content-Type
    <!-- 对象MIME类型 -->
    <input type="text" name="content-type" value="text/plain" />
    <p>
    <!-- policybase64编码值 -->
    <input type="hidden" name="policy" value="*** Provide your policy ***" />
    <!-- AK -->
    <input type="hidden" name="AccessKeyId" value="*** Provide your access key ***"/>
    <!-- 签名串信息 -->
    <input type="hidden" name="signature" value="*** Provide your signature ***"/>
    
    <input name="file" type="file" />
    <input name="submit" value="Upload" type="submit" />
    </form>
    </body>
    </html>
    • HTML表单中的policy,signature的值均是从ObsClient.createPostSignatureSync的返回结果中获取。
    • 表单HTML示例可单击此处下载:PostDemo

  3. 将生成的请求参数填入HTML页面。
  4. 选择本地文件,进行表单上传。

知识扩展

采用BrowserJS SDK直接计算签名时,AK/SK可能会展现在前端界面,有一定风险。

您还可以采用客户端-服务端模型,服务端可以采用Java、Python等SDK计算POST上传签名,客户端采用JavaScript向服务端获取签名信息后利用签名信息访问OBS。

其中,计算POST上传签名信息请参考各SDK语言:

除POST上传外,在其他场景中,为了避免前端代码直接使用AK/SK访问OBS造成敏感信息泄露,可以通过后台计算临时URL,前端使用临时URL授权访问OBS。

利用GO SDK计算临时URL,前端JS使用临时URL列举OBS桶内对象。示例如下:

  1. GO SDK后台计算列举桶临时URL。
    // 引入依赖包
    import (
           "fmt"
           "obs"
           "strings"
    )
    
    //推荐通过环境变量获取AKSK,这里也可以使用其他外部引入方式传入,如果使用硬编码可能会存在泄露风险。
    //您可以登录访问管理控制台获取访问密钥AK/SK,获取方式请参见https://support.huaweicloud.com/intl/zh-cn/usermanual-ca/ca_01_0003.html。
    var ak = os.Getenv("AccessKeyID")
    var sk = os.Getenv("SecretAccessKey")
    var endpoint = "https://your-endpoint"
    
    // 创建ObsClient结构体
    var obsClient, _ = obs.New(ak, sk, endpoint)
    
    func main() {
           input := &obs.CreateSignedUrlInput{}
           input.Expires = 3600
    
           // 生成列举对象临时URL
           // 指定为GET请求,传入桶名
           input.Method = obs.HttpMethodGet
           input.Bucket = "bucketname"
           output, _ := obsClient.CreateSignedUrl(input)
          // 获取生成的临时URL及请求头域信息
           fmt.Printf("SignedUrl:%s\n", output.SignedUrl)
           fmt.Printf("ActualSignedRequestHeaders:%v\n", output.ActualSignedRequestHeaders)
    }
  2. 前台获取到签名URL SignedUrl及请求头域信息ActualSignedRequestHeaders后,访问OBS进行列举桶操作。
    // 使用GET请求获取对象列表
    var bucketName = 'bucketname';
    var method = 'GET';
    
    // SignedUrl为上一步骤中后端服务计算得到的临时URL,
    // ActualSignedRequestHeaders为上一步骤中后端服务计算临时URL时使用的请求头域,前台实际请求应保持一致;
    var reopt = {
           method : method,
           url : SignedUrl,
           withCredentials: false, 
           headers : ActualSignedRequestHeaders || {},
           validateStatus: function(status){
                  return status >= 200;
           },
           maxRedirects : 0,
           responseType : 'text',
    };
    
    axios.request(reopt).then(function (response) {
           if(response.status < 300){                     
                  console.log('Listing object using temporary signature succeed.');              
           }else{                     
                  console.log('Listing object using temporary signature failed!');                     
                  console.log('status:' + response.status);                     
                  console.log('\n');              
           }              
           console.log(response.data);              
           console.log('\n');
    }).catch(function (err) {
           console.log('Listing object using temporary signature failed!');       
           console.log(err);       
           console.log('\n');
    });

其中,生成临时授权访问URL请参考各SDK语言: