本文档介绍分片上传文件的完整流程。

初始化分片上传

以下代码用于初始化分片上传:

__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 用于指定上传文件的名称以及上传文件所属的存储空间的名称。
  • multipartUploadInit 返回的结果中包含 UploadId,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 的大小。
  • Part 号码的范围是1~10000。如果超出这个范围,OSS 将返回 InvalidArgument 的错误码。
  • 每次上传 Part 时都要把流定位到此次上传片开头所对应的位置。
  • 每次上传 Part 之后,OSS 的返回结果会包含一个分片的 ETag值,ETag 值为 Part 数据的 MD5 值,您需要将 ETag 值和块编号组合成 PartEtag 并保存,用于后续完成分片上传。

完成分片上传

下面代码中的 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];

完成分片上传请求时可以设置 Server Callback 参数,请求完成后会向指定的 Server Address 发送回调请求。可通过返回结果的 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];
说明 如果要校验分片上传到 OSS 的文件和本地文件是否一致,可以在上传文件时携带文件的 Content-MD5 值,OSS 服务器会帮助用户进行 MD5 校验,只有在 OSS 服务器接收到的文件 MD5 值和 Content-MD5 一致时才可以上传成功,从而保证上传文件的一致性。

以下代码用于 MD5 校验设置:

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

列举分片

调用 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 作为下此读取的起点。

取消分片上传

下面代码取消了对应 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;
}