亚马逊AWS官方博客

客户端直连S3实现分片续传思路与实践

Amazon S3是互联网存储解决方案,能让所有开发人员访问同一个具备可扩展性、可靠性、安全性和快速价廉的数据存储基础设施。Amazon S3 提供了一个简单 Web 服务接口,可用于随时在 互联网上的任何位置存储和检索任何数量的数据。开发人员可以利用Amazon提供的REST API接口,命令行接口或者支持不同语言的SDK访问S3服务.

同时S3对于上传功能的API提供也是非常丰富的,与此同时,很多客户对于S3的断点续传也有了很深入的需求,本篇博客将会介绍如何使用S3的Javascript SDK来实现客户端浏览器到S3的断点续传功能.

安全考量

首先我们需要度量在浏览器客户端直连上传到S3这个场景下的安全问题,我们是一定不能把我们的AccessKey暴露到客户端浏览器的,但是上传到S3的API一定要提供AccessKey和SecretKey,因此这里我们将会利用生成临时的AccessKey和SecretKey(结合有效期)的方式来保证客户端的上传,这里介绍一篇关于利用TVM (Token Vending Machine)来生成临时Key并上传S3的文章,本文主要探讨关于S3的分片上传和断点续传的知识点.

Javascript SDK和S3 API简介

从整体编程语言架构的层面上来讲,AWS的各个语言的SDK都主要划分为上层和下层的API, 上层API主要是针对一些用户必要的功能利用下层API所作的一层封装,掌握了这个原则之后我们就可以合理的利用AWS的上层API看能否实现自身的需求.

Javascript SDK文档总结

在掌握SDK之前,我们应该先对SDK的文档和大致的结构有一个了解,这样才能方便我们更好的使用SDK, 下面列出了SDK的官网入门连接和API参考文档.

API参考文档: http://docs.thinkwithwp.com/AWSJavaScriptSDK/latest/index.html

S3 API参考文档: http://docs.thinkwithwp.com/AWSJavaScriptSDK/latest/AWS/S3.html

构建SDK中的S3对象

首先,AWS的SDK都是先需要利用Credentials来构建对象的,这里我们构建S3的对象也是如此,但是请注意一定不能将自己的Key暴露在客户端或者提交到代码中,应该使用 TVM获取了Key之后再利用AWS.Credentials对象来构建S3的对象.

在构建S3对象时,也需要同时指定AWS的Region.

利用上层Javascript API构建简单的分片断点续传功能

接下来,我们一步一步的来创建上层API构建断点续传的实践.

1. 创建工程

这里我们以node.js平台的express来提供简单的静态服务. 本文不会涉及如何安装node.js,关于安装指南,可以参考官网nodejs.org 首先利用npm包管理器安装express模版生成器:

npm install express-generator -g

完成后我们利用命令行生成项目:

mkdir s3upload
express --view=ejs

这里的--view=ejs主要指定ejs作为express的html模版引擎,方便我们的测试. 创建好之后的工程结构如下图:

2. 编写页面UI

这里我们通过引入<script src="https://sdk.amazonaws.com/js/aws-sdk-2.45.0.min.js"></script>来在浏览器端倒入AWS SDK, 并且我们创建了一个id为progress的label来监控进度. 最后我们引入了/javascript/index.js来编写我们页面的业务逻辑.

分片上传控制

接下来我们在public/javascript文件夹中创建index.js

简单的两个静态文件就能够完成分片上传的功能了.

启动项目

由于Express已经建立好模版,只需要在控制台输入npm start就能够在3000端口监听服务,通过访问http://localhost:3000 就能够测试分片上传的功能了.
完整的项目代码可以在Github上访问.

设置CORS

由于SDK通过Ajax提交数据,需要在S3桶策略中配置跨域提交的CORS. 示例中的*建议在生产环境中改成自己的域名.

S3上层API说明

以上实践为大家简单介绍了如何使用S3点上层API自动完成分片上传的功能,并且通过事件监控的方式来了解上传的进度,这里主要有以下几点需要注意.

1). 上传api需要指定Bucket: ‘testupload’, Key: file.name让SDK识别到桶和文件的Key名称.

2). 由于是上层API, 因此上传程序将会实施自动分片,由于S3的最小分片是5M,所以当文件大于5M时此上传程序才会进行自动分片,并且每隔5M为一个切片进行上传工作.

3). SDK使用AJAX方式提交自动分片的文件,因此需要设置S3的跨域提交配置CORSRule.

4). 在整个过程中,不要在客户端暴露AccessKey和Secret,利用TVM (Token Vending Machine)一文来安全获取临时Token.

通过底层API来编写实现更多分片上传的功能.

S3的分片上传主要的原理是通过初始化一次上传的uploadId, 然后在主动取消或者完成前会一直沿用这个uploadId,如果用户使用这个uploadId持续上传分片则会一直保持该文件的上传,最后通过一个complete的操作来结束上传,并且会合并所有分片成为一个文件. 通过上述原理,我们的底层API主要会调用以下来实现自定义的分片上传:

1). 首先调用createMultipartUpload

2). 在返回了uploadId之后,将文件切片后利用uploadPart API指定在同一个uploadId下从第几段开始上传.

3). 最后调用completeMultipartUpload结束上传,同时请求成功后S3将会自动合并所有分片成为一个文件.

关于各个API的详细参数,请参考官方文档.

最后,我们需要注意的是,在整个客户端上传的过程中,难免会有分片并没有全部上传完毕,或者没有最终成功调用了completeMultipartUpload的孤儿分片存在,而且随着时间的推移,这些孤儿分片也会越来越多,并且这些没有被S3合并的分片,在S3的管理控制台中是不可见的,那么我们的最佳实践应该是怎样的呢? 首先我们可以使用abortMultipartUpload来取消分片,那么某一次上传没有完成合并的分片将会被清除, 这个可以结合API来配合使用.

其次,虽然S3控制台中并不会显示未上传完成的孤儿分片,但是我们可以通过listMultipartUploads和listParts来查看未完成的分片有哪些.

最后,也是最自动的方法是我们可以使用S3的桶生命周期Policy来设置自动清除和保留未完成合并的孤儿分片的时间周期,让我们并不需要花精力来处理这些异常的情况.

总结

S3作为轻量简易高可用的存储,结合AWS的SDK,我们在通过临时证书的交互后可以轻易的实现安全的浏览器客户端直接分片断点续传到S3的功能,无论是借助于上层的S3上传API还是利用底层的createMultipartUpload实现方式,Javascript SDK都能够在各个层面给到开发人员灵活轻便的实现这些逻辑,从而让开发人员更专注在自身业务的开发工作中.

作者介绍

李磊
AWS解决方案架构师,负责基于AWS的云计算方案的架构设计,同时致力于AWS云服务在国内和全球的应用和推广。在大规模并发后台架构,电商系统,社交网络平台、互联网领域应用,DevOps以及Serverless无服务器架构等领域有着广泛的设计与实践经验。在加入AWS之前超过十年的开发和架构设计经验, 带领团队攻克各种技术挑战,总是希望站在技术的最前沿。