摘要: oss sdk 断点续传功能使用及其相关原理
前言移动端现状
随着移动端设备的硬件水平的不断提高,如今的 cpu,内存等方面都大大的超过了一般的 pc 电脑,因此在现今的程序中,合理的使用多线程去完成一些事情是非常有必要的。
多线程上传的好处
进一步占满网络资源。
进一步占满 I/O 资源。
实现原理策略
oss 有分片上传的功能,阿里云断点续传就是基于分片上传的几个 api 接口进行的封装,主要由 InitiateMultipartUpload,UploadPart,CompleteMultipartUpload,AbortMultipartUpload,ListParts 这几个组成。
流程
细节
断点续传是一个大任务,又 3 部分来完成,分别是获取 uploadId,分片上传,完成上传,这一整个连续的步骤统一在一个线程中进行。
获取 uploadId 这块需要先对本地缓存文件进行获取,如未拿到,就会直接重新生成新的 uploadId 直接去进行分片上传,否则会对记录的 id 进行之前上传了多少片进行还原,继续原来的位置继续上传。
分片上传部分,采用多线程并发上传机制,目前线程开启数量最多 5 条,根据 cpu 的核数进行判断,如果核数 < 5 会采用核数进行配置, 分片的个数最多 5000。
完成上传,对上传的 part 进行排序,需要按照自然顺序 1~n 的顺序进行上传。
文件校验,通过文件的 md5 等其他信息进行校验,分片上传中每一片也会跟服务器做 md5 校验。
进度回调机制,目前进度回调算是最基础版,目前回调原理是根据每一个分片来回调的,即当分片上传成功回调一次。
使用方式
在本地持久保存断点记录的调用方式:
android:
- String recordDirectory = Environment.getExternalStorageDirectory().getAbsolutePath() + "/oss_record/";
- File recordDir = new File(recordDirectory);
- // 要保证目录存在,如果不存在则主动创建
- if (!recordDir.exists()) {
- recordDir.mkdirs();
- }
- // 创建断点上传请求,参数中给出断点记录文件的保存位置,需是一个文件夹的绝对路径
- ResumableUploadRequest request
- = new ResumableUploadRequest("<bucketName>", "<objectKey>", "<uploadFilePath>", recordDirectory);
- // 设置上传过程回调
- request.setProgressCallback(new OSSProgressCallback<ResumableUploadRequest>() {
- @Override
- public void onProgress(ResumableUploadRequest request
- , long currentSize, long totalSize) {
- Log.d("resumableUpload", "currentSize: " + currentSize + " totalSize: " + totalSize);
- }
- });
- OSSAsyncTask resumableTask = oss.asyncResumableUpload(request
- , new OSSCompletedCallback<ResumableUploadRequest, ResumableUploadResult>() {
- @Override
- public void onSuccess(ResumableUploadRequest request, ResumableUploadResult result) {
- Log.d("resumableUpload", "success!");
- }
- @Override
- public void onFailure(ResumableUploadRequest request, ClientException clientExcepion
- , ServiceException serviceException) {
- // 异常处理
- }
- });
ios:
- // 获得UploadId进行上传,如果任务失败并且可以续传,利用同一个UploadId可以上传同一文件到同一个OSS上的存储对象
- OSSResumableUploadRequest * resumableUpload = [OSSResumableUploadRequest new];
- resumableUpload.bucketName = <bucketName > ;
- resumableUpload.objectKey = <objectKey > ;
- resumableUpload.partSize = 1024 * 1024;
- resumableUpload.uploadProgress = ^(int64_t bytesSent, int64_t totalByteSent, int64_t totalBytesExpectedToSend) {
- NSLog(@"%lld, %lld, %lld", bytesSent, totalByteSent, totalBytesExpectedToSend);
- };
- resumableUpload.uploadingFileURL = [NSURL fileURLWithPath: <your file path > ];
- NSString * cachesDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
- resumableUpload.recordDirectoryPath = cachesDir; //记录断点的文件路径
- OSSTask * resumeTask = [client resumableUpload: resumableUpload]; [resumeTask continueWithBlock: ^id(OSSTask * task) {
- if (task.error) {
- NSLog(@"error: %@", task.error);
- if ([task.error.domain isEqualToString: OSSClientErrorDomain] && task.error.code == OSSClientErrorCodeCannotResumeUpload) {
- // 该任务无法续传,需要获取新的uploadId重新上传
- }
- } else {
- NSLog(@"Upload file success");
- }
- return nil;
- }];
性能统计
数据分析
android/ios 端的分片上传改为并发后的测试与之前对比,上传分片的网络请求速度 多线程 和 单线程是一样的使用时间,这个主要是取决于带宽速度, 多线程相较于单线程主要是提高了读取文件的 io 时间。数据如下:
- iOS 模拟器测试
- 100mb大小文件
- 1000 part num 单线程 104.530217s 多线程 54.528591s
- 100 part num 单线程 59.306880s 多线程 54.336914s
- 1.31g 大小文件
- 100 part num 单线程 746.775666s 多线程 731.940330s
- 1000 part num 单线程 822.866331s 多线程 733.306236s
- 2000 part num 单线程 965.428122s 多线程 731.940330s
- 5000 part num 单线程 1205.379382s 多线程 732.982330s
- android motoXT1085 双核cpu
- 100mb文件
- 100 part num 单线程 70.484s 多线程 53.656s
- 1000 part num 单线程 104.530217s 多线程54.528591s
- 1.31g视频文件
- 135 part num 单线程 869s 多线程 738s
- 1342 part num 单线程 1079.081s 多线程 732.079s
总体来看比之前有提升,单线程随着片的个数的增加时间耗时越来越高,而多线程下,时间基本是一样的,按照目前默认配置的 part size 256kb ,单线程下网络资源与 I/O 资源都吃满,并发下性能提高平均有 30% 左右 (上传时间减少)
小结 移动端下,网络资源与 I/O 资源一般都比较紧缺,多线程不会提高网络的总带宽:比如,在跑满某个资源下载策略分配一个连接供给带宽 2000Kb/s 的时候,本地单线程 能够同时吃满 2000Kb/s,这里就到达了一个峰值;但是如果某个资源连接带宽是 2000Kb/s,但是单线程请求带宽 已经达到 2000Kb/s,那么就是本地网络带宽 Block 了上传速度,也就是说开再多线程,再多连接也都无济于事;但,如果本地网络带宽 吃完 2000Kb/s 的同时还有很多的网络资源剩余,假如还有 2000Kb/s 的提升空间,那么这时再建立一个连接 将这 2000Kb/s 也吃满,那么此时的速度就可以达到 4000Kb/s,这时提速很明显,I/O 资源同理。
后续计划
增加 crc64 编码方式进行文件正确性校验,服务端与客户端进行交互验证。
分片上传的多线程数量改为可配置,用户可以根据自己的实际需求进行设置。
进度回调优化,对进度的粒度进一步的细化,支持回调频率可配置等。
来源: http://click.aliyun.com/m/37323/