Web端直传实践

场景概述

用户通过Web上传,是BOS使用需求中很典型的一种应用场景。在该场景下,用户通常采用应用服务器中转的模型进行文件上传。

  1. 用户先将文件通过Web上传到应用服务器;
  2. 应用服务器再将文件上传到BOS。

在该模型中,存在如下3个缺点:

  • 上传速度慢。因为需要经过应用服务器中转,与客户端数据直传到BOS相比,网络传送增加了一倍。
  • 扩展性差。随着用户数量的增加,应用服务器可能成为传输瓶颈。
  • 成本高。应用服务器的部署和维护需要一定成本,如客户端数据直传到BOS,将节省应用服务器的开销,且BOS上传的流量是免费的。

因此,在该场景下,我们推荐您使用bce-bos-uploader工具实现客户端直传BOS的方式。

bce bos uploader

Baidu Cloud Engine BOS Uploader(bce-bos-uploader)是百度智能云基于Javascript SDK开发的一个ui组件,为了方便用户开发web直传应用而专门提供的,使用该工具用很少的几行代码就可以完成跟BOS服务的对接。bce-bos-uploader的demo操作界面如下:

支持的浏览器

  1. 基于Xhr2和File API,可以支持IE10+, Firefox,Chrome和Opera最新版。
  2. 借助PostObject接口,可以支持IE低版本(6,7,8,9),详细请参见进阶篇二:通过PostObject接口处理IE低版本

支持的配置参数

名称 是否必填 默认值 说明
bos_bucket Y 需要上传到的Bucket
browse_button Y 需要初始化的<input type="file"/>
uptoken_url N 用来进行计算签名的URL,需要支持JSONP
bos_endpoint N http://bj.bcebos.com BOS服务器的地址
bos_ak N 如果没有设置uptoken_url的话,必须有ak和sk这个配置才可以工作
bos_sk N 如果没有设置uptoken_url的话,必须有ak和sk这个配置才可以工作
uptoken N 如果是临时的ak和sk,必须通过这个参数设置sts token的值
multi_selection N false 是否可以选择多个文件
auto_start N false 选择文件之后,是否自动上传
max_file_size N 100M 可以选择的最大文件,超过这个值之后,会被忽略掉
bos_multipart_min_size N 10M 超过这个值之后,采用分片上传的策略。如果想让所有的文件都采用分片上传,把这个值设置为0即可
chunk_size N 4M 分片上传的时候,每个分片的大小(如果没有切换到分片上传的策略,这个值没意义)
accept N 可以支持选择的文件类型,以逗号分割的后缀名,例如:txt,pdf,doc,docx
flash_swf_url N mOxie Flash文件的地址。如果需要支持低版本的IE,必须设置这个参数

更详细的使用信息请参阅:bce-bos-uploader说明

bce-bos-upload支持默认、STS、PostObject三种签名方式。

  • 默认签名方式即使用AK/SK签名方式在浏览器中直接上传文件到BOS中。通过浏览器直传文件到BOS服务器的时候,如果把AK和SK暴露在页面中,会引发安全性的问题。 攻击者如果获取了AK和SK,可以对BOS上面的数据进行任意的操作,为了降低泄露的风险,建议用户使用STS临时认证。
  • 使用STS签名更安全灵活,它可以对用户的使用权限进行灵活、精确地控制,而且不必每次请求都调用后端接口,在有效期内就可以不用再请求新的Security Token。
  • 因为IE低版本(IE6,7,8,9)对 HTML5 API 支持的不完善,为了在这些浏览器里面实现文件直传的功能,BOS开发了PostObject接口,支持了 multipart/form-data 的请求格式,方便在低版本的IE下面把文件上传到BOS服务器。

基础篇:在浏览器中直接上传文件到BOS

使用 bce-bos-uploader,可以参考下面的内容完成如何在浏览器中直接上传文件到BOS。使用流程:

  1. 开启Bucket的跨域访问设置
  2. 查询ak/sk
  3. 初始化bce-bos-uploader参数

开启Bucket的跨域访问

受浏览器安全限制,如果想直接在浏览器中访问BOS服务,必须正确设置好相关bucket的跨域功能。设置方法如下:

  1. 登录百度智能云控制台。
  2. 选择Bucket并进入Bucket管理页面。
  3. 点击左侧『Bucket属性』,进入Bucket配置的页面。
  4. 点击右侧『CORS设置』,进入CORS设置页面。
  5. 点击『添加规则』按钮,可以添加一条或者多条CORS的规则。

查询AK/SK

在百度智能云控制台首页右上角账号下的“安全认证”查询AK和SK的信息,也可以在Bucket管理中查看。详细操作可参见管理ACCESSKEY

获取bce-bos-uploader

有两种方式可以获取bce-bos-uploader的代码:

  • 第一种:通过npm安装

    npm install @baiducloud/bos-uploader
    
  • 第二种:直接引用CDN上面的资源(测试专用,不建议用于生产环境)

    <script src=" https://bj.bcebos.com/v1/bce-cdn/lib/@baiducloud/bos-uploader/<version>/bce-bos-uploader.bundle.min.js"></script>
    

初始化bce-bos-uploader

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>bce-bos-uploader simple demo</title>
    <!--[if lt IE 8]><script src="https://unpkg.com/json3@3.3.2/lib/json3.min.js"></script><![endif]-->
    <!--[if lt IE 9]><script src="https://unpkg.com/js-polyfills@0.1.42/es5.js"></script><![endif]-->
    <!--[if lt IE 10]><script src="https://unpkg.com/mOxie@1.5.7/bin/js/moxie.min.js"></script><![endif]-->
    <script src="https://unpkg.com/jquery@3.3.1/dist/jquery.min.js"></script>
    <script src="https://bce.bdstatic.com/lib/@baiducloud/bos-uploader/1.4.0-rc.0/bce-bos-uploader.bundle.min.js"></script>
  </head>
  <body>
    <input type="file" id="file" >
    <button type="submit">开始上传</button>
    <script>
      var uploader = new baidubce.bos.Uploader({
      browse_button: '#file',
      bos_bucket: '<your bucket>',
      bos_endpoint: '<your host>',
      bos_ak: '<your ak>', 
      bos_sk: '<your sk>',
      max_file_size: '1Gb',
      init: {
        FileUploaded: function (_, file, info) {
          var bucket = info.body.bucket;
          var object = info.body.object;
          var url = '<your host>' + bucket + '/' + object;
          $(document.body).append($('<div><a href="' + url + '">' + url + '</a></div>'));
        },
        UploadComplete: function() {
          $(document.body).append('<div>上传结束!</div>');
        }
      }
    });
     $('button[type=submit]').click(function () {
      uploader.start();
      return false;
    });

    </script>
  </body>
</html>

将上述代码保存为index.html,下面会启动webserver来访问这个页面

启动webserver

  • 通过PHP来启动

    php -S 0.0.0.0:9999
    
  • 通过Python来启动

    python -m SimpleHTTPServer 9999
    
  • 其它方式请参考相关的文档

启动webserver之后,在浏览器里面访问http://localhost:9999/index.html打开刚才的页面,开始测试是否可以正常上传。

进阶篇一:STS临时认证

Bce-bos-uploader支持STS(Security Token Service)临时授权的方式。服务端生成一组具体特定操作权限、具有一定时效性的临时AK/SK,这组临时的AK/SK可以暴露给浏览器端直接使用。用户只需要将服务端返回的AK/SK及SessionToken设置为bce-bos-uploader对应的bos-ak、bos-sk和uptoken参数。
下图简单介绍了整个业务交互过程,关于STS方面的介绍请参考临时授权访问

image.png

代码实现分为应用服务器端和客户端两部分,实现过程如下:

  1. 配置应用服务器端,以Nodejs实现为例,服务器端会返回AK/SK/SessionToken。
  2. 配置浏览器,根据服务器端返回的AK/SK/SessionToken初始化bce-bos-uploader参数。

应用服务器端Nodejs实现

var http = require('http');
var url = require('url');
var util = require('util');

var STS = require('@baiducloud/sdk').STS;

var kCredentials = {
    ak: '您的AK',
    sk: '您的SK'
};

function buildStsResponse() {
    var stsClient = new STS({
        credentials: kCredentials,
        region: 'bj'
    });
    return stsClient.getSessionToken(60 * 60 * 24, {
        accessControlList: [{
            service: 'bce:bos',
            resource: ['bce-javascript-sdk-demo-test'],
            region: '*',
            effect: 'Allow',
            permission: ['READ', 'WRITE']
        }]
    }).then(function (response) {
        var body = response.body;
        return {
            AccessKeyId: body.accessKeyId,
            SecretAccessKey: body.secretAccessKey,
            SessionToken: body.sessionToken,
            Expiration: body.expiration
        };
    });
}

http.createServer(function (req, res) {
    console.log(req.url);

    var query = url.parse(req.url, true).query;

    var promise = null;

    if (query.sts) {
        promise = buildStsResponse();
    }

    promise.then(function (payload) {
        res.writeHead(200, {
            'Content-Type': 'text/javascript; charset=utf-8',
            'Access-Control-Allow-Origin': '*'
        });

        if (query.callback) {
            res.end(util.format('%s(%s)', query.callback, JSON.stringify(payload)));
        }
        else {
            res.end(JSON.stringify(payload));
        }
    });
}).listen(1337);
console.log('Server running at http://0.0.0.0:1337/');

在服务器端,用与创建bosClient实例类似的方式创建一个stsClient实例。对于stsClient实例,主要有一个方法,那就是getSessionToken。这个方法接收两个参数,第一个参数是临时授权的有效期,以秒为单位;第二个单位是具体的权限控制,参见STS服务接口

这个方法会异步访问STS授权服务器,返回一个promise对象。STS授权服务器会返回类似如下内容:

{   
    body: {         
        "accessKeyId": "d87a16e5ce1d47c1917b38ed03fbb329", 
        "secretAccessKey": "e9b6f59ce06c45cdaaea2296111dab46",
         "sessionToken": "MjUzZjQzNTY4OTE0NDRkNjg3N2E4YzJhZTc4YmU5ZDh8AAAAABwCAAB/HfHDVV2bu5xUf6rApt2YdSLG6+21UTC62EHvIuiaamtuMQQKNkR9PU2NJGVbuWgBn8Ot0atk0HnWYQGgwgyew24HtbrX3GFiR/cDymCowm0TI6OGq7k8pGuBiCczT8qZcarH7VdZBd1lkpYaXbtP7wQJqiochDXrswrCd+J/I2CeSQT6mJiMmvupUV06R89dWBL/Vcu7JQpdYBk0d5cp2B+gdaHddBobevlBmKQw50/oOykJIuho4Wn7FgOGPMPdod0Pf0s7lW/HgSnPOjZCgRl0pihs197rP3GWpnlJRyfdCY0g0GFG6T0/FsqDbxbi8lWzF1QRTmJzzh2Tax8xoPFKGMbpntp//vGP7oPYK1JoES34TjcdcZnLzIRnVIGaZAzmZMUhPEXE5RVX1w8jPEXMJJHSrFs3lJe13o9Dwg==",         
        "createTime": "2016-02-16T14:01:29Z",         
        "expiration": "2016-02-16T15:41:29Z",         
        "userId": "5e433c4a8fe74765a7ec6fc147e25c80"     
    } 
}

服务器端需要把accessKeyId、secretAccessKey、sessionToken三个字段下发给浏览器端。

配置浏览器端bce-bos-uploader参数

使用STS临时授权机制时,只需要在各个服务初始化的时候把上面所说的参数accessKeyId、secretAccessKey、sessionToken引入就可以了。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>bce-bos-uploader simple demo</title>
    <!--[if lt IE 8]><script src="http://websdk.cdn.bcebos.com/bos/json3/lib/json3.min.js"></script><![endif]-->
    <!--[if lt IE 9]><script src="http://websdk.cdn.bcebos.com/bos/js-polyfills/es5.js"></script><![endif]-->
    <!--[if lt IE 10]><script src="http://websdk.cdn.bcebos.com/bos/moxie/bin/js/moxie.js"></script><![endif]-->
    <script src="http://websdk.cdn.bcebos.com/bos/jquery/dist/jquery.min.js"></script>
    <script src="http://websdk.cdn.bcebos.com/bos/bce-bos-uploader/bce-bos-uploader.bundle.js"></script>
  </head>
  <body>

    <input type="file" id="file" >
    <script>
    var uploader = new baidubce.bos.Uploader({
      browse_button: '#file',
      bos_bucket: '<your bucket>',
      bos_endpoint: 'http://bj.bcebos.com',
      bos_ak: '<your ak>', 
      bos_sk: '<your sk>',
      uptoken: '<your sessionToken>'
    });
    </script>
  </body>
</html>

进阶篇二:通过PostObject接口处理IE低版本

因为IE低版本(IE8,IE9)对html5支持的不完善,为了在这些浏览器里面实现文件直传的功能, BOS开发了PostObject接口,通过一个multipart/form-data的格式,就可以把文件上传到BOS服务器。Postobject接口的签名模式下应用服务器端对policy生成签名,再返回给客户端。

bce-bos-uploader已经实现了对这个接口的支持,使用之前需要进行额外的配置工作:

配置应用服务器端

  1. 上传crossdomain.xml

基于html5的跨域方案,我们需要设置跨域范文(CORS);如果通过flash来完成跨域数据交互的话,需要设置crossdomain.xml,可以直接把如下内容保存为crossdomain.xml,然后上传到Bucket的根目录。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE cross-domain-policy SYSTEM "http://www.macromedia.com/xml/dtds/cross-domain-policy.dtd">
<cross-domain-policy>
  <site-control permitted-cross-domain-policies="all"/>
  <allow-access-from domain="*" secure="false" />
  <allow-http-request-headers-from domain="*" headers="*" secure="false"/>
</cross-domain-policy>

如果bucket是private的,需要设置bucket为自定义权限来允许crossdomain.xml为public-read。在Console控制台选择bucket并进入“基础设置”页签,选择“Bucket权限设置”对应的“修改配置”,修改bucket的权限设置为“自定义权限”并添加权限。

image.png

  1. 服务器端返回uptoken_url参数。

使用PostObject处理IE低版本时,需要在bce-bos-uploader中配置uptoken_url参数。

配置浏览器端bce-bos-uploader参数

  • 以html方式初始化bce-bos-uploader中的uptoken_url参数。
<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>bce-bos-uploader simple demo</title>
    <!--[if lt IE 8]><script src="./bower_components/json3/lib/json3.min.js"></script><![endif]-->
    <!--[if lt IE 9]><script src="./bower_components/js-polyfills/es5.js"></script><![endif]-->
    <!--[if lt IE 10]><script src="./bower_components/moxie/bin/js/moxie.js"></script><![endif]-->
    <script src="./bower_components/jquery/dist/jquery.min.js"></script>
    <script src="./bower_components/bce-bos-uploader/bce-bos-uploader.bundle.js"></script>
  </head>
  <body>
    <input type="file" id="file"
           data-multi_selection="true"
           data-bos_bucket="baidubce"
           data-uptoken_url="http://127.0.0.1:1337/ack" />
    <script>new baidubce.bos.Uploader('#file');</script>
  </body>
</html>
  • 以js方式初始化bce-bos-uploader中的uptoken_url参数。
<input type="file" id="file">
<script>
var uploader = new baidubce.bos.Uploader({
  browse_button: '#file',
  bos_bucket: 'baidubce',
  multi_selection: true,
  uptoken_url: 'http://127.0.0.1:133