【OSS 最佳实践】JS SDK使用STS方式实现断点续传

OSS提供JS SDK方式可以让用户直接在前端页面中嵌入js包直接操作OSS,但是该方式操作OSS最容易出现的问题即是AccessKeyId和AccessKeySecret的泄露导致被恶意访问的情况。因此提供了STS的方式动态获取token操作OSS,随之而来带来了整个业务逻辑的复杂度。今天在这里给大家介绍STS方式进行断点续传的实践方法。

基本概念

1. OSS Javascript SDK
OSS提供了海量、安全、低成本、高可靠的云存储服务,因此该产品最基本的功能即是实现将客户端的资源上传至OSS。OSS底层提供了与平台无关的RESTful API接口,但是该接口需要用户自行实现拼接发到OSS服务器的请求包以及签名参数,对于客户的技术水平提出了较高的要求。
因此OSS提供了各种语言的SDK帮助用户较方便的接入OSS并实现与OSS服务器端的对接操作,详细的SDK列表请参考OSS SDK 列表,其中绝大多数的SDK均是服务器端的SDK,是无法直接在前端页面中使用的。而Javascript SDK包括浏览器应用和node.js两种方式,浏览器应用即可以实现前端页面直接操作OSS,避免增加应用服务器的负载压力。

2. 断点续传功能
在OSS上传资源的场景中经常会遇到以下场景导致上传失败等问题,对于用户体验较差:

  • 上传超过100MB大小的文件;
  • 网络条件较差,和OSS的服务器之间的链接经常断开;
  • 上传文件需要实时掌握上传进度;
  • 业务逻辑需要断点续传。

因此OSS提供了断点续传的方法帮助用户改善该场景。断点续传的方法主要是通过checkpoint和分块上传的接口实现的断点续传。
分块上传是SDK将用户待上传的完整文件分成若干个分片后分别上传,然后上传完成后验证各个分块的etag保证数据正确性后进行合并完成的。因此分块上传的逻辑主要包括:
InitiateMultipartUpload(生成UploadId并设置Object的HTTP头)->UploadPart(上传分块,可并行上传)->CompleteMultipartUpload(根据part列表验证每个part的有效性后合并为完整的Object)。
断点续传即是在progress参数中将断点信息抛出记录在checkpoint变量中。后续续传时即将之前记录的checkpoint信息重新传入multipartUpload接口即可以实现,对应的demo请参考:

var co = require('co');
var OSS = require('ali-oss')
var client = new OSS({
  region: '<Your region>',
  accessKeyId: '<Your AccessKeyId>',
  accessKeySecret: '<Your AccessKeySecret>',
  bucket: 'Your bucket name'
});
co(function* () {
  var checkpoint;
  // retry 5 times
  for (var i = 0; i < 5; i++) {
      var result = yield client.multipartUpload('object-key', 'local-file', {
        checkpoint: checkpoint,
        progress: function* (percentage, cpt) {
          checkpoint = cpt;
        }
      });
      console.log(result);
      break; // break if success
    } catch (err) {
      console.log(err);
    }
  }
}).catch(function (err) {
  console.log(err);
});

注意:Javascript sdk对于上述的三个部分做了封装,因此对于用户来讲不需要分步操作,而仅需要统一调用multipartUpload接口即可实现。

3. STS的Token功能
OSS SDK均需要通过AccesssKeyId和AccessKeySecret授权后才可以访问OSS,而AccesssKeyId和AccessKeySecret包括三种方式,分别是:

  • 主账号AccesssKeyId和AccessKeySecret;
  • 子账号AccesssKeyId和AccessKeySecret;
  • STS动态生成的AccesssKeyId和AccessKeySecret和AccessKeyToken。

其中前两种方式对应的参数均是固定的,如果直接写在前端页面中会导致泄露的风险导致OSS被恶意操作;而第三种STS动态生成的Token则具有时间戳,仅有在时间戳有效时间内才可以使用。能够提升用户操作安全性。
STS的token一般可以前端代码向应用服务器请求得到结果,也可以使用JS SDK直接操作,demo请参考:

var OSS = require('ali-oss');
var STS = OSS.STS;
var co = require('co');
var sts = new STS({
  accessKeyId: '<子账号的AccessKeyId>',
  accessKeySecret: '<子账号的AccessKeySecret>'
});
co(function* () {
  var token = yield sts.assumeRole(
    '<role-arn>', '<policy>', '<expiration>', '<session-name>');
  var client = new OSS({
    region: '<region>',
    accessKeyId: token.credentials.AccessKeyId,
    accessKeySecret: token.credentials.AccessKeySecret,
    stsToken: token.credentials.SecurityToken,
    bucket: '<bucket-name>'
  });
}).catch(function (err) {
  console.log(err);
});

注意:使用STS的token创建OSSClient对象必须同时提供accessKeyId、accessKeySecret和stsToken,否则将报错。

JS SDK使用STS方式实现断点续传

STS的token有时间戳,当超过时间戳会导致后续的请求将无法正常请求,在断点续传时就可能出现当token已经过期后仍然需要上传的操作。为解决该问题我们建议在multipartUpload抛出的yichan异常中获取该异常重新获取token进行续传。最佳实践的代码请参考:

<!DOCTYPE html>
<html>
<head>
  <script src="http://gosspublic.alicdn.com/aliyun-oss-sdk-4.10.0.min.js"></script>
</head>
<body>
  <input type="file" id="uploadFile" />
  <script type="text/javascript">
    stsAccessKeyId = ""
    stsAccessKeySecret = ""
    stsToken = ""
    var checkpoint_temp;

    function multipartUploadWithSts(storeAs, file, cpt) {
      OSS.urllib.request("http://localhost/sts-server/sts.php", {method: 'GET'}, function (err, response) {
        if (err) {
          return alert(err);
        }
        try {
          result = JSON.parse(response);
        } catch (e) {
          errmsg = 'parse sts response info error: ' + e.message;
          return alert(errmsg);
        }
        console.log(result)
        client = new OSS.Wrapper({
          accessKeyId: result.AccessKeyId,
          accessKeySecret: result.AccessKeySecret,
          stsToken: result.SecurityToken,
          bucket: 'dongchics',
          endpoint: 'http://oss-cn-hangzhou.aliyuncs.com'

        });
        multitest(client, storeAs, file, cpt);
      })
    };

    var upload = function () {
      var client = null;
      var file = document.getElementById('uploadFile').files[0];
      console.log(file);
      var storeAs = file['name'];
      console.log("upload file=",file)
      multipartUploadWithSts(storeAs, file)
    };

    function multitest (ossClient, storeAs, file, cpt) {
      //console.log(file.name + ' => ' + storeAs);
      var checkpoint_temp;
      if (cpt) {
        console.log("multitest with cpt")
        ossClient.multipartUpload(storeAs, file,{
                                parallel: 2,
                                checkpoint: cpt,
                                progress: function* (percent, cpt) {
                                  console.log('Progress: ' + percent);
                                  checkpoint_temp = cpt
                                }
                              }).then(function (result) {
                                  console.log(result);
                                }).catch(function (err) {
                                  console.log(err);
                                  multipartUploadWithSts(storeAs, file, checkpoint_temp)
                                });
      } else {
        console.log("multitest without cpt")
        ossClient.multipartUpload(storeAs, file,{
                                parallel: 2,
                                progress: function* (percent, cpt) {
                                  console.log('Progress: ' + percent);
                                  checkpoint_temp = cpt
                                }
                              }).then(function (result) {
                                  console.log(result);
                                }).catch(function (err) {
                                  console.log(err);
                                  multipartUploadWithSts(storeAs, file, checkpoint_temp)

                                });
      }

    };
    document.getElementById('uploadFile').onchange = upload;
  </script>
</body>
</html>

这段demo主要包括一下几部分:

1.前端向服务器端发起请求sts的token的请求用户初始化OSSClient对象。而服务器端收到该请求后返回AccessKeyId、AccessKeySecret、SecurityToken和Expiration,这里使用的STS的php sdk实现的,代码可以参考:

<?php
    include_once 'aliyun-php-sdk-core/Config.php';
    use Sts\Request\V20150401 as Sts;
    function read_file($fname)
    {
        $content = '';
        if (!file_exists($fname)) {
           echo "The file $fname does not exist\n";
           exit (0);
        }
        $handle = fopen($fname, "rb");
        while (!feof($handle)) {
            $content .= fread($handle, 10000);
        }
        fclose($handle);
        return $content;
    }

    $content = read_file('./config.json');
    $myjsonarray = json_decode($content);

    $accessKeyID = $myjsonarray->AccessKeyID;
    $accessKeySecret = $myjsonarray->AccessKeySecret;
    $roleArn = $myjsonarray->RoleArn;
    $tokenExpire = $myjsonarray->TokenExpireTime;
    $policy = read_file($myjsonarray->PolicyFile);

    $iClientProfile = DefaultProfile::getProfile("cn-hangzhou", $accessKeyID, $accessKeySecret);
    $client = new DefaultAcsClient($iClientProfile);

    $request = new Sts\AssumeRoleRequest();
    $request->setRoleSessionName("client_name");
    $request->setRoleArn($roleArn);
    $request->setPolicy($policy);
    $request->setDurationSeconds($tokenExpire);
    $response = $client->doAction($request);

    $rows = array();
    $body = $response->getBody();
    $content = json_decode($body);
    $rows['status'] = $response->getStatus();
    if ($response->getStatus() == 200)
    {
        $rows['AccessKeyId'] = $content->Credentials->AccessKeyId;
        $rows['AccessKeySecret'] = $content->Credentials->AccessKeySecret;
        $rows['Expiration'] = $content->Credentials->Expiration;
        $rows['SecurityToken'] = $content->Credentials->SecurityToken;
    }
    else
    {
        $rows['AccessKeyId'] = "";
        $rows['AccessKeySecret'] = "";
        $rows['Expiration'] = "";
        $rows['SecurityToken'] = "";
    }
    echo json_encode($rows);
    return;
?>

2.在multitest方法中实现断点续传,其中cpt用来判断是否已有断点信息,如果没有该信息则初始分块上传方法,并将断点信息存放在checkpoint_temp变量中;如果有该信息则会将checkpoint_temp作为参数传入multipartUpload方法。

3.在catch异常中重复调用断点续传方法以保证token过期后继续上传。

注意:JS SDK封装的multipartUpload接口实现了并行分块上传,因此当第一个由于token过期出现的403错误后并不会立刻catch到该err,而是需要一段时间才可以捕获该异常,实际测试其延迟在网速正常的情况下为秒级或者分钟级别。

时间: 2025-01-21 06:32:49

【OSS 最佳实践】JS SDK使用STS方式实现断点续传的相关文章

【OSS 最佳实践】OSS 操作权限控制

用户操作 OSS 时是需要根据账号的 AccessKeyId 和 AccessKeySecret (后续简称 AK 和 SK )进行权限验证的,这里的 AK 和 SK 包括有多种类型:主账号的 AK 和 SK .子账号的 AK 和 SK 以及 STS 生成的临时 AK . SK 和 Token .那么他们之间有什么区别呢?具体应该如何配置使用呢?本文将带大家一起认识相关概念. 1. 概念区别 主账号的 AK 和 SK 是主账号对应的权限标识,也就是说主账号的每对 AK和 SK 是拥有账号下的所有

镜像命名的最佳实践 - 每天5分钟玩转 Docker 容器技术(18)

我们已经学会构建自己的镜像了.接下来的问题是如何在多个 Docker Host 上使用镜像. 这里有几种可用的方法: 用相同的 Dockerfile 在其他 host 构建镜像. 将镜像上传到公共 Registry(比如 Docker Hub),Host 直接下载使用. 搭建私有的 Registry 供本地 Host 使用. 第一种方法没什么特别的,前面已经讨论很多了.我们将讨论如何使用公共和私有 Registry 分发镜像. 为镜像命名 无论采用何种方式保存和分发镜像,首先都得给镜像命名. 当

SQLServer · 最佳实践 · 数据库实现大容量插入的几种方式

背景 很多用户在使用阿里云云数据库SQL Server时,为了加快插入速度,都尝试使用大容量插入的方式,大家都知道,对于完整恢复模式下的数据库,大容量导入执行的所有行插入操作都会完整地记录在事务日志中.如果使用完整恢复模式,大型数据导入会导致填充事务日志的速度很快.相反,对于简单恢复模式或大容量日志恢复模式,大容量导入操作的按最小方式记录日志减少了大容量导入操作填满日志空间的可能性.另外,按最小方式记录日志的效率也比按完整方式记录日志高 . 但实际上,当大容量导入与数据库镜像共存时,会出现镜像

JS trim去空格的最佳实践_javascript技巧

刚好上次有同学提出疑问.刚好可以自测一下.先来看看老道在<JavaScript 精粹>P33 写的吧.他对 String 对象扩展了一个 trim() 方法: 复制代码 代码如下: Function.prototype.method = function(name, func) { this.prototype[name] = func; return this; }; String.method('trim', function() { return this.replace(/^\s+|\

《社交网站界面设计(原书第2版)》——1.5 实践(或最佳实践):做事的习惯性行为或方式

1.5 实践(或最佳实践):做事的习惯性行为或方式 最佳实践很有意思,常常与原则或交互模式相混淆.它们是连续的统一体,并且比交互模式方案的规定更少--至少在我们的定义中是这样的.我们常常会在交互模式中提供最佳实践. 例如:在移动背景中,为触摸进行设计.确保按钮.表单和其他元素足够大,让用户可以与它们交互,并且可以容纳人的手指,以免不小心触发相邻的元素. 最佳实践有助于澄清如何应用一个具体设计方案,而且它通常也是最有效.高效地解决问题的方式,虽然它并不一定是唯一的处理方式.

HybridDB · 最佳实践 · 阿里云数据库PetaData

前言 随着互联网DT时代的高速发展,业界需要简单高效的数据处理方式在海量数据中挖掘价值,企业厂商和开源界目前较流行的的做法,是提供支持类SQL接口的数据库服务,或者是提供SDK接口的数据处理平台服务. 在SQL接口的数据库服务中,传统的关系数据库,如MySQL.PG等,处理海量数据显得越来越力不从心,既无法突破单机硬件资源限制,又无法并行利用多机硬件资源:大数据NewSQL数据库,必须依赖外部数据库保证数据的事务特性,并通过数据导入工具将完整提交的数据导入进来计算,系统复杂度和成本较高. 在SD

微信公众平台开发最佳实践(第2版)

  <微信公众平台开发最佳实践 第2版>微信公众平台开发经典之作全新改版,精心挑选最经典的商业项目开发,成千上万人次微信公众平台开发者从中受益 方倍工作室已出版本书的第3版电子版,内容更多,价格更优惠,欢迎查看购买,地址:http://www.cnblogs.com/txw1958/p/buy-ebook.html       前言 出版说明 自从方倍工作室在博客园推出微信公众平台开发系列教程后,受到广大微信开发人员及爱好者的热情关注,相关文章的日访问量高达2万人次,而<微信公众平台开发

微信公众平台开发最佳实践

<微信公众平台开发最佳实践>共分10章,案例程序采用广泛流行的PHP.MySQL.XML.CSS.JavaScript.HTML5等程序语言及数据库实现.系统完整地介绍微信公众平台基础接口.自定义菜单.高级接口.微信支付.分享转发等所有相关技术,以生活类.娱乐类.企业类微信开发为切入点,讲解了30多个功能或应用案例. 前言 自从方倍工作室在博客园推出微信公众平台开发系列教程后,受到广大微信开发人员及爱好者的热情关注,相头文章的日访问量高达上万人次,而<微信公众平台开发入门教程>一门

【 CDN 最佳实践】CDN 命中率优化思路

CDN 在静态资源的加速场景中是将静态资源缓存在距离客户端较近的CDN 节点上,然后客户端访问该资源即可通过较短的链路直接从缓存中获取资源,而避免再通过较长的链路回源获取静态资源.因此 CDN的缓存命中率的高低直接影响客户体验,而保证较高的命中率也成为了站长的核心命题.在本文中我们就一起探讨 CDN 缓存命中率的概念.影响因素以及优化策略. 1.缓存命中率的概念 CDN 的缓存命中率包括两种:字节缓存命中率和请求缓存命中率.其中字节缓存命中率是指 CDN 缓存命中 Response 的字节数除以