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

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

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,而是需要一段时间才可以捕获该异常,实际测试其延迟在网速正常的情况下为秒级或者分钟级别。

相关实践学习
对象存储OSS快速上手——如何使用ossbrowser
本实验是对象存储OSS入门级实验。通过本实验,用户可学会如何用对象OSS的插件,进行简单的数据存、查、删等操作。
目录
相关文章
|
9月前
|
存储 JavaScript API
百宝箱开放平台 ✖️ Node.js SDK
开发者可以通过安装 Node.js SDK 的方式将百宝箱的 OpenAPI 集成到自有系统中,从而在外部系统中发起智能体对话。
522 0
百宝箱开放平台 ✖️ Node.js SDK
|
JavaScript 前端开发 开发工具
【Azure Developer】使用JavaScript通过SDK进行monitor-query的client认证报错问题
AADSTS90002: Tenant 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' not found. Check to make sure you have the correct tenant ID and are signing into the correct cloud. Check with your subscription administrator, this may happen if there are no active subscriptions for the tenant.
221 1
|
缓存 监控 JavaScript
日志服务(SLS) 的桌面端 Node.js SDK 封装
日志服务(SLS) 的桌面端 Node.js SDK 封装
16719 7
|
前端开发 算法 Java
使用虹软SDK实现离线人脸注册,人脸登录(H5-JS前端,java后台)
一开始找人脸识别的第三方接口,选择了百度,就是发请求给百度的接口,解析人家返回的数据。
|
JavaScript 对象存储
阿里云 OSS 如何通过 Node.js 上传图片 #49
阿里云 OSS 如何通过 Node.js 上传图片 #49
530 0
|
JavaScript 对象存储
vue2.6,js集成oss后图片上传成功后,下载图片打开显示图片损坏
我确认本地上传的图片没问题,我用的put直传,拿到返回的url,换了几张图都这样
vue2.6,js集成oss后图片上传成功后,下载图片打开显示图片损坏
|
存储 JavaScript 安全
阿里 OSS图片上传 —— 原生JS中使用
这是最近开发遇到的一个坑,&quot;可爱&quot;的前辈们留下来的宝贵财产,很不幸的是,一行注释都没给,所以开心(被迫)的去研究了一下文档. 然而发现官方文档又臭又长,于是自己捣鼓了下,总算是搞出来了.
1633 0
阿里 OSS图片上传 —— 原生JS中使用
|
JavaScript Java 开发工具
JS:Vue项目浏览器直接上传文件到阿里云OSS
JS:Vue项目浏览器直接上传文件到阿里云OSS
2293 0
|
传感器 监控 JavaScript
NodeJS & Dapr Javascript SDK 官方使用指南
用于在 JavaScript 和 TypeScript 中构建 Dapr 应用程序的客户端库。该客户端抽象了公共 Dapr API,例如服务到服务调用、状态管理、发布/订阅、机密等,并为构建应用程序提供了一个简单、直观的 API。
640 0
NodeJS & Dapr Javascript SDK 官方使用指南
|
边缘计算 开发框架 JavaScript
使用 Dapr JS SDK 让 Nest.js 集成 Dapr
Dapr 是一个可移植的、事件驱动的运行时,它使任何开发人员能够轻松构建出弹性的、无状态和有状态的应用程序,并可运行在云平台或边缘计算中,它同时也支持多种编程语言和开发框架。
514 0
使用 Dapr JS SDK 让 Nest.js 集成 Dapr