全部產品
Search
文件中心

Object Storage Service:C++分區上傳

更新時間:Jun 19, 2024

OSS提供的分區上傳(Multipart Upload)功能,將要上傳的較大檔案(Object)分成多個分區(Part)來分別上傳,上傳完成後再調用CompleteMultipartUpload介面將這些Part組合成一個Object來達到斷點續傳的效果。

注意事項

  • 本文以華東1(杭州)外網Endpoint為例。如果您希望通過與OSS同地區的其他阿里雲產品訪問OSS,請使用內網Endpoint。關於OSS支援的Region與Endpoint的對應關係,請參見訪問網域名稱和資料中心

  • 本文以OSS網域名稱建立OSSClient為例。如果您希望通過自訂網域名、STS等方式建立OSSClient,請參見建立OssClient

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

分區上傳流程

分區上傳(Multipart Upload)分為以下三個步驟:

  1. 初始化一個分區上傳事件。

    調用InitiateMultipartUpload方法返回OSS建立的全域唯一的uploadId。

  2. 上傳分區。

    調用UploadPart方法上傳分區資料。

    說明
    • 對於同一個uploadId,分區號(PartNumber)標識了該分區在整個檔案內的相對位置。如果使用同一個分區號上傳了新的資料,則OSS上該分區已有的資料將會被覆蓋。

    • OSS將收到的分區資料的MD5值放在ETag頭內返回給使用者。

    • OSS計算上傳資料的MD5值,並與SDK計算的MD5值比較,如果不一致則返回InvalidDigest錯誤碼。

  3. 完成分區上傳。

    所有分區上傳完成後,調用CompleteMultipartUpload方法將所有分區合并成完整的檔案。

分區上傳完整樣本

以下通過一個完整的樣本對分區上傳的流程進行逐步解析:

#include <alibabacloud/oss/OssClient.h>
#include <fstream>

int64_t getFileSize(const std::string& file)
{
    std::fstream f(file, std::ios::in | std::ios::binary);
    f.seekg(0, f.end);
    int64_t size = f.tellg();
    f.close();
    return size;
}

using namespace AlibabaCloud::OSS;

int main(void)
{
    /* 初始化OSS帳號資訊 */
    
    std::string Endpoint = "yourEndpoint";
    /* 填寫Bucket名稱,例如examplebucket */
    std::string BucketName = "examplebucket";
    /* 填寫Object完整路徑,完整路徑中不能包含Bucket名稱,例如exampledir/exampleobject.txt。 */
    std::string ObjectName = "exampledir/exampleobject.txt";

    /* 初始化網路等資源 */
    InitializeSdk();

    ClientConfiguration conf;
    /* 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
    OssClient client(Endpoint, credentialsProvider, conf);
  
    InitiateMultipartUploadRequest initUploadRequest(BucketName, ObjectName);
    /*(可選)請參見如下樣本設定儲存類型 */
    //initUploadRequest.MetaData().addHeader("x-oss-storage-class", "Standard");

    /* 初始化分區上傳事件 */
    auto uploadIdResult = client.InitiateMultipartUpload(initUploadRequest);
    /* 根據UploadId執行取消分區上傳事件或者列舉已上傳分區的操作。*/
    /* 如果您需要根據您需要UploadId執行取消分區上傳事件的操作,您需要在調用InitiateMultipartUpload完成初始化分區之後擷取uploadId。*/
    /* 如果您需要根據您需要UploadId執行列舉已上傳分區的操作,您需要在調用InitiateMultipartUpload完成初始化分區之後,且在調用CompleteMultipartUpload完成分區上傳之前擷取uploadId。*/ 
    auto uploadId = uploadIdResult.result().UploadId();
    std::string fileToUpload = "yourLocalFilename";
    int64_t partSize = 100 * 1024;
    PartList partETagList;
    auto fileSize = getFileSize(fileToUpload);
    int partCount = static_cast<int>(fileSize / partSize);
    /* 計算分區個數 */
    if (fileSize % partSize != 0) {
        partCount++;
    }

    /* 對每一個分區進行上傳 */
    for (int i = 1; i <= partCount; i++) {
        auto skipBytes = partSize * (i - 1);
        auto size = (partSize < fileSize - skipBytes) ? partSize : (fileSize - skipBytes);
        std::shared_ptr<std::iostream> content = std::make_shared<std::fstream>(fileToUpload, std::ios::in|std::ios::binary);
        content->seekg(skipBytes, std::ios::beg);

        UploadPartRequest uploadPartRequest(BucketName, ObjectName, content);
        uploadPartRequest.setContentLength(size);
        uploadPartRequest.setUploadId(uploadId);
        uploadPartRequest.setPartNumber(i);
        auto uploadPartOutcome = client.UploadPart(uploadPartRequest);
        if (uploadPartOutcome.isSuccess()) {
            Part part(i, uploadPartOutcome.result().ETag());
            partETagList.push_back(part);
        }
        else {
            std::cout << "uploadPart fail" <<
            ",code:" << uploadPartOutcome.error().Code() <<
            ",message:" << uploadPartOutcome.error().Message() <<
            ",requestId:" << uploadPartOutcome.error().RequestId() << std::endl;
        }

    }

    /* 完成分區上傳 */
    /* 在執行完成分區上傳操作時,需要提供所有有效partETags。OSS收到提交的partETags後,會逐一驗證每個分區的有效性。當所有的資料分區驗證通過後,OSS將把這些分區組合成一個完整的檔案。*/
    CompleteMultipartUploadRequest request(BucketName, ObjectName);
    request.setUploadId(uploadId);
    request.setPartList(partETagList);
    /*(可選)請參見如下樣本設定讀寫權限ACL */
    //request.setAcl(CannedAccessControlList::Private);

    auto outcome = client.CompleteMultipartUpload(request);

    if (!outcome.isSuccess()) {
        /* 異常處理 */
        std::cout << "CompleteMultipartUpload fail" <<
        ",code:" << outcome.error().Code() <<
        ",message:" << outcome.error().Message() <<
        ",requestId:" << outcome.error().RequestId() << std::endl;
        return -1;
    }

    /* 釋放網路等資源 */
    ShutdownSdk();
    return 0;
}

擷取已經上傳的分區過程中調用CompleteMultipartUpload介面時,需提供每個分區的ETag值。您可以通過以下兩種方式擷取ETag值:

  • 上傳每個分區時,返回結果中會包含這個分區的ETag值,供您直接儲存使用。上述樣本採用的是此種方式。

  • 通過調用ListParts方法擷取已經上傳的分區的ETag值。

列舉已上傳的分區

調用ListParts方法列舉出指定UploadID下所有已上傳成功的分區。

說明

預設情況下,簡單列舉已上傳的分區只能一次列舉1000個分區。當分區數量大於1000時,請選擇分頁列舉所有已上傳分區。

簡單列舉已上傳的分區

以下代碼用於簡單列舉已上傳的分區:

#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;

int main(void)
{
    /* 初始化OSS帳號資訊 */
    
    std::string Endpoint = "yourEndpoint";
    /* 填寫Bucket名稱,例如examplebucket */
    std::string BucketName = "examplebucket";
    /* 填寫Object完整路徑,完整路徑中不能包含Bucket名稱,例如exampledir/exampleobject.txt。*/
    std::string ObjectName = "exampledir/exampleobject.txt";
    /* 填寫UploadId。UploadId來源於調用InitiateMultipartUpload完成初始化分區之後,且在調用CompleteMultipartUpload完成分區上傳之前的返回結果。*/
    std::string UploadId = "yourUploadId";

    /* 初始化網路等資源 */
    InitializeSdk();

    ClientConfiguration conf;
    /* 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
    OssClient client(Endpoint, credentialsProvider, conf);

    /* 列舉已上傳分區,預設列舉1000個分區 */
    ListPartsRequest listuploadrequest(BucketName, ObjectName);
    listuploadrequest.setUploadId(UploadId);

    bool IsTruncated = false;

    do {
        auto listUploadResult = client.ListParts(listuploadrequest);
        if (!listUploadResult.isSuccess()) {
            /* 異常處理 */
            std::cout << "ListParts fail" <<
            ",code:" << listUploadResult.error().Code() <<
            ",message:" << listUploadResult.error().Message() <<
            ",requestId:" << listUploadResult.error().RequestId() << std::endl;
            break;
        }
        else {
            for (const auto& part : listUploadResult.result().PartList()) {
                std::cout << "part"<<
                ",name:" << part.PartNumber() <<
                ",size:" << part.Size() <<
                ",etag:" << part.ETag() <<
                ",lastmodify time:" << part.LastModified() << std::endl;
            }
        }
        listuploadrequest.setPartNumberMarker(listUploadResult.result().NextPartNumberMarker());
        IsTruncated = listUploadResult.result().IsTruncated();
    } while (IsTruncated);

    /* 釋放網路等資源 */
    ShutdownSdk();
    return 0;
}

分頁列舉所有已上傳的分區

以下代碼用於指定每頁分區的數量,並分頁列舉所有分區:

#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;

int main(void)
{
    /* 初始化OSS帳號資訊 */
    
    std::string Endpoint = "yourEndpoint";
    /* 填寫Bucket名稱,例如examplebucket */
    std::string BucketName = "examplebucket";
    /* 填寫Object完整路徑,完整路徑中不能包含Bucket名稱,例如exampledir/exampleobject.txt。 */
    std::string ObjectName = "exampledir/exampleobject.txt";
    /* 填寫UploadId。UploadId來源於調用InitiateMultipartUpload完成初始化分區之後,且在調用CompleteMultipartUpload完成分區上傳之前的返回結果。*/
    std::string UploadId = "yourUploadId";

    /* 初始化網路等資源 */
    InitializeSdk();

    ClientConfiguration conf;
    /* 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
    OssClient client(Endpoint, credentialsProvider, conf);

    /* 分頁列舉全部上傳的分區 */
    /* 設定每頁列舉最大上傳分區數目 */
    ListPartsRequest listuploadrequest(BucketName, ObjectName);
    listuploadrequest.setMaxParts(50);
    listuploadrequest.setUploadId(UploadId);
    bool IsTruncated = false;
    do {
        auto listuploadresult = client.ListParts(listuploadrequest);
        if (!listuploadresult.isSuccess()) {
            /* 異常處理 */
            std::cout << "ListParts fail" <<
            ",code:" << listuploadresult.error().Code() <<
            ",message:" << listuploadresult.error().Message() <<
            ",requestId:" << listuploadresult.error().RequestId() << std::endl;
            break;
        }
        else {
            for (const auto& part : listuploadresult.result().PartList()) {
                std::cout << "part"<<
                ",name:" << part.PartNumber() <<
                ",size:" << part.Size() <<
                ",etag:" << part.ETag() <<
                ",lastmodify time:" << part.LastModified() << std::endl;
            }
        }  
        listuploadrequest.setPartNumberMarker(listuploadresult.result().NextPartNumberMarker());
        IsTruncated = listuploadresult.result().IsTruncated();
    } while (IsTruncated);

    /* 釋放網路等資源 */
    ShutdownSdk();
    return 0;
}

列舉分區上傳事件

調用ListMultipartUploads方法列舉所有執行中的分區上傳事件,即已初始化但尚未完成或已取消的分區上傳事件。

說明

預設情況下,簡單列舉分區上傳事件只能一次列舉1000個分區上傳事件。當分區數量大於1000時,請選擇分頁列舉所有上傳事件。

簡單列舉分區上傳事件

以下代碼用於簡單列舉分區上傳事件。

#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;

int main(void)
{
    /* 初始化OSS帳號資訊 */
    
    std::string Endpoint = "yourEndpoint";
    /* 填寫Bucket名稱,例如examplebucket */
    std::string BucketName = "examplebucket";    

    /* 初始化網路等資源 */
    InitializeSdk();

    ClientConfiguration conf;
    /* 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
    OssClient client(Endpoint, credentialsProvider, conf);

    /* 列舉已上傳事件,預設列舉1000個分區 */
    ListMultipartUploadsRequest listmultiuploadrequest(BucketName);
	bool IsTruncated = false;
    do {
        auto listresult = client.ListMultipartUploads(listmultiuploadrequest);
        if (!listresult.isSuccess()) {
            /* 異常處理 */
            std::cout << "ListMultipartUploads fail" <<
            ",code:" << listresult.error().Code() <<
            ",message:" << listresult.error().Message() <<
            ",requestId:" << listresult.error().RequestId() << std::endl;
            break;
        }
        else {
            for (const auto& part : listresult.result().MultipartUploadList()) {
                std::cout << "part"<<
                ",name:" << part.Key <<
                ",uploadid:" << part.UploadId <<
                ",initiated time:" << part.Initiated << std::endl;
            }
        }
        listmultiuploadrequest.setKeyMarker(listresult.result().NextKeyMarker()); 
        listmultiuploadrequest.setUploadIdMarker(listresult.result().NextUploadIdMarker());
        IsTruncated = listresult.result().IsTruncated();
    } while (IsTruncated);

    /* 釋放網路等資源 */
    ShutdownSdk();
    return 0;
}

分頁列舉所有上傳事件

以下代碼用於分頁列舉所有上傳事件:

#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;

int main(void)
{
    /* 初始化OSS帳號資訊 */
    
    std::string Endpoint = "yourEndpoint";
    /* 填寫Bucket名稱,例如examplebucket */
    std::string BucketName = "examplebucket"; 

    /* 初始化網路等資源 */
    InitializeSdk();

    ClientConfiguration conf;
    /* 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
    OssClient client(Endpoint, credentialsProvider, conf);

    /* 分頁列舉全部上傳事件 */
    /* 設定每頁列舉最大上傳事件數目 */
    ListMultipartUploadsRequest  listmultiuploadrequest(BucketName);
    listmultiuploadrequest.setMaxUploads(50);
    bool IsTruncated = false;
    do {
        auto listresult = client.ListMultipartUploads(listmultiuploadrequest);
        if (!listresult.isSuccess()) {
            /* 異常處理 */
            std::cout << "ListMultipartUploads fail" <<
            ",code:" << listresult.error().Code() <<
            ",message:" << listresult.error().Message() <<
            ",requestId:" << listresult.error().RequestId() << std::endl;
            break;
        }
        else {
            for (const auto& part : listresult.result().MultipartUploadList()) {
                std::cout << "part"<<
                ",name:" << part.Key <<
                ",uploadid:" << part.UploadId <<
                ",initiated time:" << part.Initiated << std::endl;
            }
        }  
        listmultiuploadrequest.setKeyMarker(listresult.result().NextKeyMarker()); 
        listmultiuploadrequest.setUploadIdMarker(listresult.result().NextUploadIdMarker());
        IsTruncated = listresult.result().IsTruncated();
    } while (IsTruncated);

    /* 釋放網路等資源 */
    ShutdownSdk();
    return 0;
}

取消分區上傳事件

您可以調用client.AbortMultipartUpload方法來取消分區上傳事件。當一個分區上傳事件被取消後,無法再使用同一個uploadId執行任何操作,已經上傳的分區資料會被刪除。

以下代碼用於取消分區上傳事件:

#include <alibabacloud/oss/OssClient.h>
using namespace AlibabaCloud::OSS;

int main(void)
{
    /* 初始化OSS帳號資訊 */
    
    std::string Endpoint = "yourEndpoint";
    /* 填寫Bucket名稱,例如examplebucket */
    std::string BucketName = "examplebucket";
    /* 填寫Object完整路徑,完整路徑中不能包含Bucket名稱,例如exampledir/exampleobject.txt。 */
    std::string ObjectName = "exampledir/exampleobject.txt";
    /* 填寫UploadId。UploadId來源於調用InitiateMultipartUpload完成初始化分區之後的返回結果。*/
    std::string UploadId = "yourUploadId";

    /* 初始化網路等資源 */
    InitializeSdk();

    ClientConfiguration conf;
    /* 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
    OssClient client(Endpoint, credentialsProvider, conf);
  
    InitiateMultipartUploadRequest initUploadRequest(BucketName, ObjectName);

    /* 初始化分區上傳事件 */
    auto uploadIdResult = client.InitiateMultipartUpload(initUploadRequest);
    auto uploadId = uploadIdResult.result().UploadId();

    /* 取消分區上傳 */
    AbortMultipartUploadRequest  abortUploadRequest(BucketName, ObjectName, uploadId);
    auto outcome = client.AbortMultipartUpload(abortUploadRequest);

    if (!outcome.isSuccess()) {
        /* 異常處理 */
        std::cout << "AbortMultipartUpload fail" <<
        ",code:" << outcome.error().Code() <<
        ",message:" << outcome.error().Message() <<
        ",requestId:" << outcome.error().RequestId() << std::endl;
        return -1;
    }

    /* 釋放網路等資源 */
    ShutdownSdk();
    return 0;
}

相關文檔

  • 關於分區上傳的完整範例程式碼,請參見GitHub樣本

  • 分區上傳的完整實現涉及三個API介面,詳情如下:

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

  • 關於列舉已上傳分區的API介面說明,請參見ListParts

  • 關於列舉所有執行中的分區上傳事件(即已初始化但尚未完成或已取消的分區上傳事件)的API介面說明,請參見ListMultipartUploads