先上一篇阿里的官方文档。
比较简单的文件上传和下载参考官方文档就可以了,这主要介绍一下OSS的“异步分片上传”。因为文档中的分片上传,是使用的同步上传,这样分片上传就没什么意义了。
首先导入 AliyunOSSiOS SDK,手动和pods 两种方式都支持。
然后配置OSS连接,需要用到的参数:endPoint(例如:https: //oss-cn-shenzhen.aliyuncs.com)、AccessKey、SecretKey、bucketName(觉得可以理解成在云存储中的文件夹),bucketName每个用户的Bucket数量不能超过10个。
实现普通的上传和下载文件(支持URL和文件Data两种方式),连接参数没有错误的前提下,copy官方文档中的代码就可以实现。
实现异步分片上传。
1、初始化分片上传。
指定UploadId、PartNum,UploadId是用来区分每次分片上传的标示。
- (void)_initPart {
_uploadId = @"";
_partInfos = @[].mutableCopy;
NSString * uploadToBucket = bucketName;
NSString * uploadObjectkey = multipartUploadKey;
OSSInitMultipartUploadRequest * init = [OSSInitMultipartUploadRequest new];
init.bucketName = uploadToBucket;
init.objectKey = uploadObjectkey;
// init.contentType = @"application/octet-stream";
OSSTask * initTask = [_client multipartUploadInit:init];
[initTask waitUntilFinished];
if (!initTask.error) {
OSSInitMultipartUploadResult * result = initTask.result;
_uploadId = result.uploadId;
} else {
NSLog(@"multipart upload failed, error: %@", initTask.error);
return;
}
}
2、对文件进行分片处理
注意:每一片的大小都要大于100KB
- (NSArray *)cutFileForFragments:(NSData *)data withPath:(NSURL *)pathUrl {
NSUInteger offset = 500 * 1024; // 单位 K 分片
NSUInteger fileSize = data.length;
// 块数
NSUInteger chunks = (fileSize%offset==0)?(fileSize/offset):(fileSize/(offset) + 1);
NSMutableArray *fragments = [[NSMutableArray alloc] initWithCapacity:0];
for (NSUInteger i = 0; i < chunks; i ++) {
NSData* data;
NSFileHandle *readHandle = [NSFileHandle fileHandleForReadingFromURL:pathUrl error:nil];
[readHandle seekToFileOffset:offset * i];
data = [readHandle readDataOfLength:offset];
[fragments addObject:data];
}
return fragments;
}
3、进行异步分片上传
使用了dispatch_group和线程锁,监听所有异步上传的完成操作 和保证partNum与分片数组对应。
注意:PartNum的范围是1~10000,在这碰到过坑,千万别以0开头!!!不然会使上传的文件读不出来。
- (void)uploadPartObjectAsync:(NSArray *)datas {
dispatch_group_t group = dispatch_group_create();
// 分片上传
for (int i = 1; i <= datas.count; i++) {
dispatch_group_enter(group);
OSSUploadPartRequest * uploadPart = [OSSUploadPartRequest new];
uploadPart.bucketName = bucketName;
uploadPart.objectkey = multipartUploadKey;
uploadPart.uploadId = _uploadId;
uploadPart.partNumber = i; // part number start from 1
uploadPart.uploadPartData = datas[i-1];
OSSTask * uploadPartTask = [_client uploadPart:uploadPart];
[uploadPartTask continueWithBlock:^id(OSSTask *uploadPartTask) {
NSLog(@"objectKey: %@", uploadPart.objectkey);
if (!uploadPartTask.error) {
OSSUploadPartResult * result = uploadPartTask.result;
NSLog(@"+++++++++++++++ %d", i);
uint64_t fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:uploadPart.uploadPartFileURL.absoluteString error:nil] fileSize];
@synchronized (_partInfos) { // NSMutableArray 是线程不安全的,所以加个同步锁
OSSPartInfo *partInfo = [OSSPartInfo partInfoWithPartNum:i eTag:result.eTag size:fileSize];
NSLog(@"分片 part的标示:************** %d", partInfo.partNum);
[_partInfos addObject:partInfo];
}
dispatch_group_leave(group);
} else {
NSLog(@"upload part error: %@", uploadPartTask.error);
dispatch_group_leave(group);
}
return nil;
}];
}
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"分片上传完成! %@", _partInfos);
// 对异步上传的part进行排序
[self sortPart];
[self ossComplete];
});
}
4、根据partNum对分片数组中的元素进行排序
因为每次上传part之后,OSS的返回结果会包含一个分片的ETag,值为part数据的MD5值,需要将它和partNum组合成PartEtag保存
使用简单的冒泡排序
- (void)sortPart {
for(int i=0; i<_partInfos.count; i++){
for(int j=i; j<_partInfos.count; j++){
OSSPartInfo *iPartInfo = _partInfos[i];
OSSPartInfo *jPartInfo = _partInfos[j];
if(iPartInfo.partNum > jPartInfo.partNum){
[_partInfos exchangeObjectAtIndex:i withObjectAtIndex:j];
}
}
}
}
5、调用上传完成操作
OSS会将这些part组合成一个完整的文件
- (void)ossComplete{
// 上传完成
OSSCompleteMultipartUploadRequest * complete = [OSSCompleteMultipartUploadRequest new];
complete.bucketName = bucketName;
complete.objectKey = multipartUploadKey;
complete.uploadId = _uploadId;
complete.partInfos = _partInfos;
OSSTask * completeTask = [_client completeMultipartUpload:complete];
[[completeTask continueWithBlock:^id(OSSTask *task) {
if (!task.error) {
OSSCompleteMultipartUploadResult * result = task.result;
dispatch_async(dispatch_get_main_queue(), ^{
_textView.text = [NSString stringWithFormat:@"分片上传成功 result ++++ : %@", result];
});
} else {
dispatch_async(dispatch_get_main_queue(), ^{
_textView.text = [NSString stringWithFormat:@"分片上传失败 error:------ %@", task.error];
});
}
return nil;
}] waitUntilFinished];
}
到这里异步分片上传就完成了,上传成功后会返回文件所在URL,copy URL在浏览器中贴一下就能把上传的文件下载,可以用这个方法来验证是否上传成功。
最后附上demo