下面演示通过分片上传文件的整个流程:

初始化分片上传

__block NSString * uploadId = nil;
__block NSMutableArray * partInfos = [NSMutableArray new];
NSString * uploadToBucket = @"<bucketName>";
NSString * uploadObjectkey = @"<objectKey>";
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;
}
  • OSSInitMultipartUploadRequest指定上传文件的所属存储空间Bucket和文件名字。
  • multipartUploadInit返回的结果中包含UploadId,是区分分片上传的唯一标示,后面的操作中会用到。

上传分片

NSString * filePath = [docDir stringByAppendingPathComponent:@"***"];
//要上传的文件
int chuckCount = *;
//分片上传数量
uint64_t offset = fileSie/chuckCount;
//分片大小
for (int i = 1; i <= chuckCount; i++) {
	OSSUploadPartRequest * uploadPart = [OSSUploadPartRequest new];
	uploadPart.bucketName = uploadToBucket;
	uploadPart.objectkey = uploadObjectkey;
	uploadPart.uploadId = uploadId;
	uploadPart.partNumber = i; // part number start from 1

	NSFileHandle* readHandle = [NSFileHandle fileHandleForReadingAtPath:filePath];
        [readHandle seekToFileOffset:offset * (i -1)];
        
        NSData* data = [readHandle readDataOfLength:offset];
        uploadPart.uploadPartData = data;

	OSSTask * uploadPartTask = [client uploadPart:uploadPart];

	[uploadPartTask waitUntilFinished];

	if (!uploadPartTask.error) {
		OSSUploadPartResult * result = uploadPartTask.result;
		uint64_t fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:uploadPart.uploadPartFileURL.absoluteString error:nil] fileSize];
		[partInfos addObject:[OSSPartInfo partInfoWithPartNum:i eTag:result.eTag size:fileSize]];
	} else {
		NSLog(@"upload part error: %@", uploadPartTask.error);
		return;
	}
}

上述代码调用uploadPart来上传每一个分片。

注意
  • 每一个分片上传请求需指定UploadId和PartNum。
  • uploadPart要求除最后一个Part外,其他的Part大小都要大于100KB。但是Upload Part接口并不会立即校验上传。Part的大小(因为不知道是否为最后一块);只有当Complete Multipart Upload的时候才会校验。
  • Part号码的范围是1~10000。如果超出这个范围,OSS将返回InvalidArgument的错误码。
  • 每次上传part时都要把流定位到此次上传片开头所对应的位置。
  • 每次上传part之后,OSS的返回结果会包含一个分片的ETag,值为part数据的MD5值,您需要将它和块编号组合成PartEtag保存,后续完成分片上传需要用到。

MD5校验设置

因为移动端网络环境的复杂性,OSS提供了基于端到端的文件验证功能。如果要校验分片上传到OSS的文件和本地文件是否一致,可以在上传文件时携带文件的Content-MD5值,OSS服务器会帮助用户进行MD5校验,只有在OSS服务器接收到的文件MD5值和Content-MD5一致时才可以上传成功,从而保证上传文件的一致性。

OSSUploadPartRequest * uploadPart = [OSSUploadPartRequest new];
uploadPart.bucketName = TEST_BUCKET;
uploadPart.uploadId = uploadId;
....
uploadPart.contentMd5 = [OSSUtil fileMD5String:filepath];

完成分片上传

下面代码中的 partInfos就是进行分片上传中保存的partETag的列表,OSS收到用户提交的Part列表后,会逐一验证每个数据Part的有效性。当所有的数据Part验证通过后,OSS会将这些part组合成一个完整的文件。

OSSCompleteMultipartUploadRequest * complete = [OSSCompleteMultipartUploadRequest new];
complete.bucketName = uploadToBucket;
complete.objectKey = uploadObjectkey;
complete.uploadId = uploadId;
complete.partInfos = partInfos;

OSSTask * completeTask = [client completeMultipartUpload:complete];

[[completeTask continueWithBlock:^id(OSSTask *task) {
	if (!task.error) {
		OSSCompleteMultipartUploadResult * result = task.result;
		// ...
	} else {
		// ...
	}
	return nil;
}] waitUntilFinished];

完成分片上传(设置ServerCallback)

完成分片上传请求可以设置Server Callback参数,请求完成后会向指定的Server Adress发送回调请求;可通过查看返回结果的result.serverReturnJsonString,查看servercallback结果。

OSSCompleteMultipartUploadRequest * complete = [OSSCompleteMultipartUploadRequest new];
complete.bucketName = @"<bucketName>";
complete.objectKey = @"<objectKey>";
complete.uploadId = uploadId;
complete.partInfos = partInfos;
complete.callbackParam = @{
                          @"callbackUrl": @"<server address>",
                          @"callbackBody": @"<test>"
                          };
complete.callbackVar = @{
                        @"var1": @"value1",
                        @"var2": @"value2"
                        };
OSSTask * completeTask = [client completeMultipartUpload:complete];
[[completeTask continueWithBlock:^id(OSSTask *task) {
    if (!task.error) {
        OSSCompleteMultipartUploadResult * result = task.result;
        NSLog(@"server call back return : %@", result.serverReturnJsonString);
    } else {
        // ...
    }
    return nil;
}] waitUntilFinished];

删除分片上传事件

下面代码取消了对应UploadId的分片上传请求。

OSSAbortMultipartUploadRequest * abort = [OSSAbortMultipartUploadRequest new];
abort.bucketName = @"<bucketName>";
abort.objectKey = @"<objectKey>";
abort.uploadId = uploadId;

OSSTask * abortTask = [client abortMultipartUpload:abort];

[abortTask waitUntilFinished];

if (!abortTask.error) {
	OSSAbortMultipartUploadResult * result = abortTask.result;
	uploadId = result.uploadId;
} else {
	NSLog(@"multipart upload failed, error: %@", abortTask.error);
	return;
}

列举分片

调用listParts方法获取某个上传事件所有已上传的分片。

OSSListPartsRequest * listParts = [OSSListPartsRequest new];
listParts.bucketName = @"<bucketName>";
listParts.objectKey = @"<objectkey>";
listParts.uploadId = @"<uploadid>";

OSSTask * listPartTask = [client listParts:listParts];

[listPartTask continueWithBlock:^id(OSSTask *task) {
	if (!task.error) {
		NSLog(@"list part result success!");
		OSSListPartsResult * listPartResult = task.result;
		for (NSDictionary * partInfo in listPartResult.parts) {
			NSLog(@"each part: %@", partInfo);
		}
	} else {
		NSLog(@"list part result error: %@", task.error);
	}
	return nil;
}];
注意 默认情况下,如果存储空间中的分片上传事件的数量大于1000,则只会返回1000个Multipart Upload信息,且返回结果中 IsTruncated 为false,并返回 NextPartNumberMarker作为下此读取的起点。