OSS的分片上傳(Multipart Upload)用于將要上傳的較大的文件(Object)分成多個數據塊(OSS中稱之為Part)來分別上傳,上傳完成后再調用CompleteMultipartUpload接口將這些Part組合成一個Object。
注意事項
使用本文示例前您需要先通過自定義域名、STS等方式新建OSSClient,具體請參見初始化。
說明所創建存儲空間的所屬地域取決于初始化配置的endpoint地域信息。
要上傳文件,您必須有
oss:PutObject
權限。具體操作,請參見為RAM用戶授權自定義的權限策略。
分片上傳流程
分片上傳的基本流程如下:
將要上傳的文件按照一定的大小分片。
初始化一個分片上傳任務(InitiateMultipartUpload)。
逐個或并行上傳分片(UploadPart)。
完成上傳(CompleteMultipartUpload)。
該過程需注意以下幾點:
除了最后一塊Part,其他Part的大小不能小于100 KB,否則會導致調用CompleteMultipartUpload接口失敗。
要上傳的文件切分成Part之后,文件順序是通過上傳過程中指定的partNumber來確定的,實際執行中并沒有順序要求,因此可以實現并發上傳。
具體的并發個數并不是越多速度越快,要結合用戶自身的網絡情況和設備負載綜合考慮。網絡情況較好時,建議增大分片大小。反之,減小分片大小。
默認情況下,已經上傳但還沒有調用CompleteMultipartUpload的Part是不會被自動回收的,因此如果要終止上傳并刪除占用的空間請調用AbortMultipartUpload。如果需要自動回收上傳的Part,請參見生命周期管理。
分片上傳完整示例
以下通過一個完整的示例對分片上傳的流程進行逐步解析:
__block NSString * uploadId = nil;
__block NSMutableArray * partInfos = [NSMutableArray new];
// 填寫Bucket名稱,例如examplebucket。
NSString * uploadToBucket = @"examplebucket";
// 填寫Object完整路徑,例如exampledir/exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
NSString * uploadObjectkey = @"exampledir/exampleobject.txt";
// OSSInitMultipartUploadRequest用于指定上傳文件的名稱以及上傳文件所屬的存儲空間的名稱。
OSSInitMultipartUploadRequest * init = [OSSInitMultipartUploadRequest new];
init.bucketName = uploadToBucket;
init.objectKey = uploadObjectkey;
// init.contentType = @"application/octet-stream";
// multipartUploadInit返回的結果中包含UploadId,UploadId是分片上傳的唯一標識。
OSSTask * initTask = [client multipartUploadInit:init];
[initTask waitUntilFinished];
if (!initTask.error) {
OSSInitMultipartUploadResult * result = initTask.result;
uploadId = result.uploadId;
// 根據uploadId執行取消分片上傳事件或者列舉已上傳分片的操作。
// 如果您需要根據您需要uploadId執行取消分片上傳事件的操作,您需要在調用InitiateMultipartUpload完成初始化分片之后獲取uploadId。
// 如果您需要根據您需要uploadId執行列舉已上傳分片的操作,您需要在調用InitiateMultipartUpload完成初始化分片之后,且在調用CompleteMultipartUpload完成分片上傳之前獲取uploadId。
//NSLog(@"UploadId": %@, uploadId);
} else {
NSLog(@"multipart upload failed, error: %@", initTask.error);
return;
}
// 指定要上傳的文件。
NSString * filePath = @"<filepath>";
// 獲取文件大小。
uint64_t fileSize = [[[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil] fileSize];
// 設置分片上傳數量。
int chuckCount = 10;
// 設置分片大小。
uint64_t offset = fileSize/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;
}
}
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];
本地文件分片上傳
分片上傳完整示例是按照分片上傳流程逐步實現的完整代碼,本地文件分片上傳的代碼是將分片上傳完整示例中的代碼進行了封裝,您只需要使用MultipartUploadRequest即可實現分片上傳。
以分片上傳的方式上傳本地文件的代碼示例如下。
// 填寫Bucket名稱,例如examplebucket。
NSString *bucketName = @"examplebucket";
// 填寫文件完整路徑,例如exampledir/exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
NSString *objectKey = @"exampledir/exampleobject.txt";
OSSMultipartUploadRequest * multipartUploadRequest = [OSSMultipartUploadRequest new];
multipartUploadRequest.bucketName = bucketName;
multipartUploadRequest.objectKey = objectKey;
// 設置分片大小,默認256 KB。
multipartUploadRequest.partSize = 1024 * 1024;
multipartUploadRequest.uploadProgress = ^(int64_t bytesSent, int64_t totalByteSent, int64_t totalBytesExpectedToSend) {
NSLog(@"progress: %lld, %lld, %lld", bytesSent, totalByteSent, totalBytesExpectedToSend);
};
multipartUploadRequest.uploadingFileURL = [[NSBundle mainBundle] URLForResource:@"wangwang" withExtension:@"zip"];
OSSTask * multipartTask = [client multipartUpload:multipartUploadRequest];
[[multipartTask continueWithBlock:^id(OSSTask *task) {
if (task.error) {
NSLog(@"error: %@", task.error);
} else {
NSLog(@"Upload file success");
}
return nil;
}] waitUntilFinished];
列舉已上傳分片
調用listParts
方法獲取某個上傳事件所有已上傳的分片。
OSSListPartsRequest * listParts = [OSSListPartsRequest new];
// 填寫Bucket名稱,例如examplebucket。
listParts.bucketName = @"examplebucket";
// 填寫文件完整路徑,例如exampledir/exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
listParts.objectKey = @"exampledir/exampleobject.txt";
// 填寫uploadId。uploadId來源于調用InitiateMultipartUpload完成初始化分片之后,且在調用CompleteMultipartUpload完成分片上傳之前的返回結果。
listParts.uploadId = @"0004B999EF518A1FE585B0C9****";
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;
}];
// waitUntilFinished會阻塞當前線程,但是不會阻塞上傳任務進程。
// [listPartTask waitUntilFinished];
默認情況下,如果存儲空間中的分片上傳事件的數量大于1000,則OSS僅返回1000個Multipart Upload信息,且返回結果中IsTruncated的值為false,并返回NextPartNumberMarker作為下次讀取的起點。
取消分片上傳
以下代碼用于取消了對應UploadId
的分片上傳請求。
OSSAbortMultipartUploadRequest * abort = [OSSAbortMultipartUploadRequest new];
// 填寫Bucket名稱,例如examplebucket。
abort.bucketName = @"examplebucket";
// 填寫文件完整路徑,例如exampledir/exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
abort.objectKey = @"exampledir/exampleobject.txt";
// 填寫uploadId。uploadId來源于調用InitiateMultipartUpload完成初始化分片之后的返回結果。
abort.uploadId = @"0004B999EF518A1FE585B0C9****";
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;
}
// waitUntilFinished會阻塞當前線程,但是不會阻塞上傳任務進程。
// [abortTask waitUntilFinished];
相關文檔
關于分片上傳的完整示例代碼,請參見GitHub示例。
分片上傳的完整實現涉及三個API接口,詳情如下:
關于初始化分片上傳事件的API接口說明,請參見InitiateMultipartUpload。
關于分片上傳Part的API接口說明,請參見UploadPart。
關于完成分片上傳的API接口說明,請參見CompleteMultipartUpload。
關于列舉已上傳分片的API接口說明,請參見ListParts。
關于取消分片上傳事件的API接口說明,請參見AbortMultipartUpload。