全部產品
Search
文件中心

Object Storage Service:iOS分區上傳

更新時間:Aug 28, 2024

OSS的分區上傳(Multipart Upload)用於將要上傳的較大的檔案(Object)分成多個資料區塊(OSS中稱之為Part)來分別上傳,上傳完成後再調用CompleteMultipartUpload介面將這些Part組合成一個Object。

注意事項

  • 使用本文樣本前您需要先通過自訂網域名、STS等方式建立OSSClient,具體請參見初始化

    說明

    所建立儲存空間的所屬地區取決於初始化配置的endpoint地區資訊。

  • 要上傳檔案,您必須有oss:PutObject許可權。具體操作,請參見為RAM使用者授權自訂的權限原則

分區上傳流程

分區上傳的基本流程如下:

  1. 將要上傳的檔案按照一定的大小分區。

  2. 初始化一個分區上傳任務(InitiateMultipartUpload)。

  3. 逐個或並行上傳分區(UploadPart)。

  4. 完成上傳(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介面說明,請參見ListParts

  • 關於取消分區上傳事件的API介面說明,請參見AbortMultipartUpload