全部產品
Search
文件中心

Object Storage Service:分區上傳

更新時間:Nov 09, 2024

在上傳大檔案(超過5 GB)到OSS的過程中,如果出現網路中斷、程式異常退出等問題導致檔案上傳失敗,您需要使用分區上傳的方式上傳大檔案。分區上傳通過將待上傳的大檔案分成多個較小的片段(Part),充分利用網路頻寬和伺服器資源並發上傳多個Part,加快上傳完成時間,並在Part上傳完成之後調用CompleteMultipartUpload介面將這些Part組合成一個完整的Object。

前提條件

使用情境

  • 大檔案加速上傳

    當檔案大小超過5 GB時,使用分區上傳可實現並行上傳多個Part以加快上傳速度。

  • 網路環境較差

    網路環境較差時,建議使用分區上傳。當出現上傳失敗的時候,您僅需重傳失敗的Part。

  • 檔案大小不確定

    可以在需要上傳的檔案大小還不確定的情況下開始上傳,這種情境在視頻監控等行業應用中比較常見。

分區上傳流程

流程說明如下:

說明

如果您需要完成分區上傳的完整流程,包括初始化分區上傳,上傳分區以及將分區合為完成的Object,您需要oss:PutObject許可權。具體操作,請參見為RAM使用者授權自訂的權限原則

  1. 將待上傳檔案按照一定大小進行分區。

  2. 使用InitiateMultipartUpload介面初始化一個分區上傳任務。

  3. 使用UploadPart介面上傳分區。

    檔案切分成Part之後,檔案順序是通過上傳過程中指定的partNumber來確定,所以您可以並發上傳這些片段。並發數並非越多越快,請結合自身網路狀況和裝置負載綜合考慮。

    如果您希望終止上傳任務,可調用AbortMultipartUpload介面,成功上傳的Part會一併刪除。

  4. 使用CompleteMultipartUpload介面將Part組合成一個Object。

使用限制

限制項

規格

檔案大小

不超過48.8 TB

Part數量

1~10, 000個

單個Part大小

最小值為100 KB,最大值為5 GB。最後一個Part的大小允許小於100 KB。

單次ListParts請求返回的Part最大數量

1, 000個

單次ListMultipartUploads請求返回的Multipart Upload事件最大數量

1, 000個

注意事項

  • 通過分區上傳的方式上傳檔案時,一次只能上傳一個檔案,且不支援上傳目錄。

  • 降低PUT類請求費用

    如果要上傳的檔案數量較多,直接指定上傳的檔案類型為深度冷歸檔類型會造成較高的PUT類請求費用。建議您先將檔案的儲存類型指定為標準儲存進行上傳,然後通過生命週期規則將其轉儲為深度冷歸檔類型,從而降低PUT類請求費用。

  • 檔案上傳效能調優

    如果您在上傳大量檔案時,在命名上使用了順序首碼(如時間戳記或字母順序),可能會出現大量檔案索引集中儲存於儲存空間中某個特定分區的情況。此時如果您的請求速率過大,會導致請求速率下降。建議您在上傳大量檔案時,不要使用順序首碼的檔案名稱。更多資訊,請參見OSS效能最佳實務

  • 檔案覆蓋

    上傳同名檔案會覆蓋OSS中已有檔案。您可以通過以下方式防止檔案被意外覆蓋:

    • 開啟版本控制功能

      開啟版本控制功能後,被覆蓋的檔案會以歷史版本的形式儲存下來,您可以隨時恢複歷史版本檔案。更多資訊,請參見版本控制介紹

    • 在上傳請求中攜帶禁止覆蓋同名檔案的參數

      在上傳請求的header中攜帶x-oss-forbid-overwrite參數,並指定其值為true。當您上傳的檔案在OSS中存在同名檔案時,該檔案會上傳失敗,並返回FileAlreadyExists錯誤。更多資訊,請參見InitiateMultipartUpload

操作步驟

使用阿里雲SDK

以下僅列舉常見SDK的分區上傳的程式碼範例。關於其他SDK的分區上傳的程式碼範例,請參見SDK簡介

import com.aliyun.oss.ClientException;
import com.aliyun.oss.OSS;
import com.aliyun.oss.common.auth.*;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.OSSException;
import com.aliyun.oss.internal.Mimetypes;
import com.aliyun.oss.model.*;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

public class Demo {

    public static void main(String[] args) throws Exception {
        // Endpoint以華東1(杭州)為例,其它Region請按實際情況填寫。
        String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
        // 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
        EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();
        // 填寫Bucket名稱,例如examplebucket。
        String bucketName = "examplebucket";
        // 填寫Object完整路徑,例如exampledir/exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
        String objectName = "exampledir/exampleobject.txt";
        // 待上傳本地檔案路徑。
        String filePath = "D:\\localpath\\examplefile.txt";
        // 填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為cn-hangzhou。
        String region = "cn-hangzhou";

        // 建立OSSClient執行個體。
        ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
        clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);        
        OSS ossClient = OSSClientBuilder.create()
        .endpoint(endpoint)
        .credentialsProvider(credentialsProvider)
        .clientConfiguration(clientBuilderConfiguration)
        .region(region)               
        .build();
        
        try {
            // 建立InitiateMultipartUploadRequest對象。
            InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest(bucketName, objectName);

            // 如果需要在初始化分區時佈建要求頭,請參考以下範例程式碼。
             ObjectMetadata metadata = new ObjectMetadata();
            // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
            // 指定該Object的網頁緩衝行為。
            // metadata.setCacheControl("no-cache");
            // 指定該Object被下載時的名稱。
            // metadata.setContentDisposition("attachment;filename=oss_MultipartUpload.txt");
            // 指定該Object的內容編碼格式。
            // metadata.setContentEncoding(OSSConstants.DEFAULT_CHARSET_NAME);
            // 指定初始化分區上傳時是否覆蓋同名Object。此處設定為true,表示禁止覆蓋同名Object。
            // metadata.setHeader("x-oss-forbid-overwrite", "true");
            // 指定上傳該Object的每個part時使用的伺服器端加密方式。
            // metadata.setHeader(OSSHeaders.OSS_SERVER_SIDE_ENCRYPTION, ObjectMetadata.KMS_SERVER_SIDE_ENCRYPTION);
            // 指定Object的密碼編譯演算法。如果未指定此選項,表明Object使用AES256密碼編譯演算法。
            // metadata.setHeader(OSSHeaders.OSS_SERVER_SIDE_DATA_ENCRYPTION, ObjectMetadata.KMS_SERVER_SIDE_ENCRYPTION);
            // 指定KMS託管的使用者主要金鑰。
            // metadata.setHeader(OSSHeaders.OSS_SERVER_SIDE_ENCRYPTION_KEY_ID, "9468da86-3509-4f8d-a61e-6eab1eac****");
            // 指定Object的儲存類型。
            // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard);
            // 指定Object的對象標籤,可同時設定多個標籤。
            // metadata.setHeader(OSSHeaders.OSS_TAGGING, "a:1");
            // request.setObjectMetadata(metadata);

            // 根據檔案自動化佈建ContentType。如果不設定,ContentType預設值為application/oct-srream。
            if (metadata.getContentType() == null) {
                metadata.setContentType(Mimetypes.getInstance().getMimetype(new File(filePath), objectName));
            }

            // 初始化分區。
            InitiateMultipartUploadResult upresult = ossClient.initiateMultipartUpload(request);
            // 返回uploadId。
            String uploadId = upresult.getUploadId();
            // 根據uploadId執行取消分區上傳事件或者列舉已上傳分區的操作。
            // 如果您需要根據uploadId執行取消分區上傳事件的操作,您需要在調用InitiateMultipartUpload完成初始化分區之後擷取uploadId。 
            // 如果您需要根據uploadId執行列舉已上傳分區的操作,您需要在調用InitiateMultipartUpload完成初始化分區之後,且在調用CompleteMultipartUpload完成分區上傳之前擷取uploadId。
            // System.out.println(uploadId);

            // partETags是PartETag的集合。PartETag由分區的ETag和分區號組成。
            List<PartETag> partETags =  new ArrayList<PartETag>();
            // 每個分區的大小,用於計算檔案有多少個分區。單位為位元組。
            final long partSize = 1 * 1024 * 1024L;   //1 MB。

            // 根據上傳的資料大小計算分區數。以本地檔案為例,說明如何通過File.length()擷取上傳資料的大小。
            final File sampleFile = new File(filePath);
            long fileLength = sampleFile.length();
            int partCount = (int) (fileLength / partSize);
            if (fileLength % partSize != 0) {
                partCount++;
            }
            // 遍曆分區上傳。
            for (int i = 0; i < partCount; i++) {
                long startPos = i * partSize;
                long curPartSize = (i + 1 == partCount) ? (fileLength - startPos) : partSize;
                UploadPartRequest uploadPartRequest = new UploadPartRequest();
                uploadPartRequest.setBucketName(bucketName);
                uploadPartRequest.setKey(objectName);
                uploadPartRequest.setUploadId(uploadId);
                // 設定上傳的分區流。
                // 以本地檔案為例說明如何建立FIleInputstream,並通過InputStream.skip()方法跳過指定資料。
                InputStream instream = new FileInputStream(sampleFile);
                instream.skip(startPos);
                uploadPartRequest.setInputStream(instream);
                // 設定分區大小。除了最後一個分區沒有大小限制,其他的分區最小為100 KB。
                uploadPartRequest.setPartSize(curPartSize);
                // 設定分區號。每一個上傳的分區都有一個分區號,取值範圍是1~10000,如果超出此範圍,OSS將返回InvalidArgument錯誤碼。
                uploadPartRequest.setPartNumber( i + 1);
                // 每個分區不需要按順序上傳,甚至可以在不同用戶端上傳,OSS會按照分區號排序組成完整的檔案。
                UploadPartResult uploadPartResult = ossClient.uploadPart(uploadPartRequest);
                // 每次上傳分區之後,OSS的返回結果包含PartETag。PartETag將被儲存在partETags中。
                partETags.add(uploadPartResult.getPartETag());
            }


            // 建立CompleteMultipartUploadRequest對象。
            // 在執行完成分區上傳操作時,需要提供所有有效partETags。OSS收到提交的partETags後,會逐一驗證每個分區的有效性。當所有的資料分區驗證通過後,OSS將把這些分區組合成一個完整的檔案。
            CompleteMultipartUploadRequest completeMultipartUploadRequest =
                    new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);

            // 如果需要在完成分區上傳的同時設定檔案存取權限,請參考以下範例程式碼。
            // completeMultipartUploadRequest.setObjectACL(CannedAccessControlList.Private);
            // 指定是否列舉當前UploadId已上傳的所有Part。僅在Java SDK為3.14.0及以上版本時,支援通過服務端List分區資料來合并完整檔案時,將CompleteMultipartUploadRequest中的partETags設定為null。
            // Map<String, String> headers = new HashMap<String, String>();
            // 如果指定了x-oss-complete-all:yes,則OSS會列舉當前UploadId已上傳的所有Part,然後按照PartNumber的序號排序並執行CompleteMultipartUpload操作。
            // 如果指定了x-oss-complete-all:yes,則不允許繼續指定body,否則報錯。
            // headers.put("x-oss-complete-all","yes");
            // completeMultipartUploadRequest.setHeaders(headers);

            // 完成分區上傳。
            CompleteMultipartUploadResult completeMultipartUploadResult = ossClient.completeMultipartUpload(completeMultipartUploadRequest);
            System.out.println(completeMultipartUploadResult.getETag());
        } catch (OSSException oe) {
            System.out.println("Caught an OSSException, which means your request made it to OSS, "
                    + "but was rejected with an error response for some reason.");
            System.out.println("Error Message:" + oe.getErrorMessage());
            System.out.println("Error Code:" + oe.getErrorCode());
            System.out.println("Request ID:" + oe.getRequestId());
            System.out.println("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            System.out.println("Caught an ClientException, which means the client encountered "
                    + "a serious internal problem while trying to communicate with OSS, "
                    + "such as not being able to access the network.");
            System.out.println("Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }
}
<?php
if (is_file(__DIR__ . '/../autoload.php')) {
    require_once __DIR__ . '/../autoload.php';
}
if (is_file(__DIR__ . '/../vendor/autoload.php')) {
    require_once __DIR__ . '/../vendor/autoload.php';
}

use OSS\Credentials\EnvironmentVariableCredentialsProvider;
use OSS\OssClient;
use OSS\CoreOssException;
use OSS\Core\OssUtil;

// 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。 
$provider = new EnvironmentVariableCredentialsProvider();
// Endpoint以華東1(杭州)為例,其它Region請按實際情況填寫。
$endpoint = 'https://oss-cn-hangzhou.aliyuncs.com';
// 填寫Bucket名稱,例如examplebucket。
$bucket= 'examplebucket';
//填寫不包含Bucket名稱在內的Object完整路徑,例如exampledir/exampleobject.txt。
$object = 'exampledir/exampleobject.txt';
// 填寫本地檔案的完整路徑。
$uploadFile = 'D:\\localpath\\examplefile.txt';
$initOptions = array(
    OssClient::OSS_HEADERS  => array(
        // 指定該Object被下載時的網頁緩衝行為。
        // 'Cache-Control' => 'no-cache',
        // 指定該Object被下載時的名稱。
        // 'Content-Disposition' => 'attachment;filename=oss_download.jpg',
        // 指定該Object被下載時的內容編碼格式。
        // 'Content-Encoding' => 'utf-8',
        // 指定到期時間,單位為毫秒。
        // 'Expires' => 150,
        // 指定初始化分區上傳時是否覆蓋同名Object。此處設定為true,表示禁止覆蓋同名Object。
        //'x-oss-forbid-overwrite' => 'true',
        // 指定上傳該Object的每個part時使用的伺服器端加密方式。
        // 'x-oss-server-side-encryption'=> 'KMS',
        // 指定Object的密碼編譯演算法。
        // 'x-oss-server-side-data-encryption'=>'SM4',
        // 指定KMS託管的使用者主要金鑰。
        //'x-oss-server-side-encryption-key-id' => '9468da86-3509-4f8d-a61e-6eab1eac****',
        // 指定Object的儲存類型。
        // 'x-oss-storage-class' => 'Standard',
        // 指定Object的對象標籤,可同時設定多個標籤。
        // 'x-oss-tagging' => 'TagA=A&TagB=B',
    ),
);

/**
 *  步驟1:初始化一個分區上傳事件,並擷取uploadId。
 */
try{
    $config = array(
        "provider" => $provider,
        "endpoint" => $endpoint,
        "signatureVersion" => OssClient::OSS_SIGNATURE_VERSION_V4,
        "region"=> "cn-hangzhou"
    );
    $ossClient = new OssClient($config);
    //返回uploadId。uploadId是分區上傳事件的唯一標識,您可以根據uploadId發起相關的操作,如取消分區上傳、查詢分區上傳等。
    $uploadId = $ossClient->initiateMultipartUpload($bucket, $object, $initOptions);
    print("initiateMultipartUpload OK" . "\n");
    // 根據uploadId執行取消分區上傳事件或者列舉已上傳分區的操作。
    // 如果您需要根據您需要uploadId執行取消分區上傳事件的操作,您需要在調用InitiateMultipartUpload完成初始化分區之後擷取uploadId。 
    // 如果您需要根據您需要uploadId執行列舉已上傳分區的操作,您需要在調用InitiateMultipartUpload完成初始化分區之後,且在調用CompleteMultipartUpload完成分區上傳之前擷取uploadId。
    //print("UploadId: " . $uploadId . "\n");
} catch(OssException $e) {
    printf($e->getMessage() . "\n");
    return;
}

/*
 * 步驟2:上傳分區。
 */
$partSize = 10 * 1024 * 1024;
$uploadFileSize = sprintf('%u',filesize($uploadFile));
$pieces = $ossClient->generateMultiuploadParts($uploadFileSize, $partSize);
$responseUploadPart = array();
$uploadPosition = 0;
$isCheckMd5 = true;
foreach ($pieces as $i => $piece) {
    $fromPos = $uploadPosition + (integer)$piece[$ossClient::OSS_SEEK_TO];
    $toPos = (integer)$piece[$ossClient::OSS_LENGTH] + $fromPos - 1;
    $upOptions = array(
        // 上傳檔案。
        $ossClient::OSS_FILE_UPLOAD => $uploadFile,
        // 設定分區號。
        $ossClient::OSS_PART_NUM => ($i + 1),
        // 指定分區上傳起始位置。
        $ossClient::OSS_SEEK_TO => $fromPos,
        // 指定檔案長度。
        $ossClient::OSS_LENGTH => $toPos - $fromPos + 1,
        // 是否開啟MD5校正,true為開啟。
        $ossClient::OSS_CHECK_MD5 => $isCheckMd5,
    );
    // 開啟MD5校正。
    if ($isCheckMd5) {
        $contentMd5 = OssUtil::getMd5SumForFile($uploadFile, $fromPos, $toPos);
        $upOptions[$ossClient::OSS_CONTENT_MD5] = $contentMd5;
    }
    try {
        // 上傳分區。
        $responseUploadPart[] = $ossClient->uploadPart($bucket, $object, $uploadId, $upOptions);
        printf("initiateMultipartUpload, uploadPart - part#{$i} OK\n");
    } catch(OssException $e) {
        printf("initiateMultipartUpload, uploadPart - part#{$i} FAILED\n");
        printf($e->getMessage() . "\n");
        return;
    }

}
// $uploadParts是由每個分區的ETag和分區號(PartNumber)組成的數組。
$uploadParts = array();
foreach ($responseUploadPart as $i => $eTag) {
    $uploadParts[] = array(
        'PartNumber' => ($i + 1),
        'ETag' => $eTag,
    );
}
/**
 * 步驟3:完成上傳。
 */
$comOptions['headers'] = array(
    // 指定完成分區上傳時是否覆蓋同名Object。此處設定為true,表示禁止覆蓋同名Object。
    // 'x-oss-forbid-overwrite' => 'true',
    // 如果指定了x-oss-complete-all:yes,則OSS會列舉當前uploadId已上傳的所有Part,然後按照PartNumber的序號排序並執行CompleteMultipartUpload操作。
    // 'x-oss-complete-all'=> 'yes'
);

try {
    // 執行completeMultipartUpload操作時,需要提供所有有效$uploadParts。OSS收到提交的$uploadParts後,會逐一驗證每個分區的有效性。當所有的資料分區驗證通過後,OSS將把這些分區組合成一個完整的檔案。
    $ossClient->completeMultipartUpload($bucket, $object, $uploadId, $uploadParts,$comOptions);
    printf( "Complete Multipart Upload OK\n");
}  catch(OssException $e) {
    printf("Complete Multipart Upload FAILED\n");
    printf($e->getMessage() . "\n");
    return;
}
           
const OSS = require('ali-oss');
const path = require("path");

const client = new OSS({
  // yourregion填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為oss-cn-hangzhou。
  region: 'yourregion',
  // 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
  accessKeyId: process.env.OSS_ACCESS_KEY_ID,
  accessKeySecret: process.env.OSS_ACCESS_KEY_SECRET,
  authorizationV4: true,
  // 填寫儲存空間名稱。
  bucket: 'yourbucketname',
});


const progress = (p, _checkpoint) => {
  // Object的上傳進度。
  console.log(p); 
  // 分區上傳的斷點資訊。
  console.log(_checkpoint); 
};

const headers = {  
  // 指定Object的儲存類型。
  'x-oss-storage-class': 'Standard', 
  // 指定Object標籤,可同時設定多個標籤。
  'x-oss-tagging': 'Tag1=1&Tag2=2', 
  // 指定初始化分區上傳時是否覆蓋同名Object。此處設定為true,表示禁止覆蓋同名Object。
  'x-oss-forbid-overwrite': 'true'
}

// 開始分區上傳。
async function multipartUpload() {
  try {
    // 依次填寫Object完整路徑(例如exampledir/exampleobject.txt)和本地檔案的完整路徑(例如D:\\localpath\\examplefile.txt)。Object完整路徑中不能包含Bucket名稱。
    // 如果本地檔案的完整路徑中未指定本地路徑(例如examplefile.txt),則預設從樣本程式所屬專案對應本地路徑中上傳檔案。
    const result = await client.multipartUpload('exampledir/exampleobject.txt', path.normalize('D:\\localpath\\examplefile.txt'), {
      progress,
      // headers,
      // 指定meta參數,自訂Object的中繼資料。通過head介面可以擷取到Object的meta資料。
      meta: {
        year: 2020,
        people: 'test',
      },
    });
    console.log(result);
    // 填寫Object完整路徑,例如exampledir/exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
    const head = await client.head('exampledir/exampleobject.txt');
    console.log(head);
  } catch (e) {
    // 捕獲逾時異常。
    if (e.code === 'ConnectionTimeoutError') {
      console.log('TimeoutError');
      // do ConnectionTimeoutError operation
    }
    console.log(e);
  }
}

multipartUpload();
# -*- coding: utf-8 -*-
import os
from oss2 import SizedFileAdapter, determine_part_size
from oss2.models import PartInfo
import oss2
from oss2.credentials import EnvironmentVariableCredentialsProvider

# 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())

# 填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。
endpoint = "https://oss-cn-hangzhou.aliyuncs.com"

# 填寫Endpoint對應的Region資訊,例如cn-hangzhou。注意,v4簽名下,必須填寫該參數
region = "cn-hangzhou"

# yourBucketName填寫儲存空間名稱。
bucket = oss2.Bucket(auth, endpoint, "yourBucketName", region=region)

# 填寫不能包含Bucket名稱在內的Object完整路徑,例如exampledir/exampleobject.txt。
key = 'exampledir/exampleobject.txt'
# 填寫本地檔案的完整路徑,例如D:\\localpath\\examplefile.txt。
filename = 'D:\\localpath\\examplefile.txt'

total_size = os.path.getsize(filename)
# determine_part_size方法用於確定分區大小。
part_size = determine_part_size(total_size, preferred_size=100 * 1024)

# 初始化分區。
# 如需在初始化分區時設定檔案儲存體類型,請在init_multipart_upload中設定相關Headers,參考如下。
# headers = dict()
# 指定該Object的網頁緩衝行為。
# headers['Cache-Control'] = 'no-cache'
# 指定該Object被下載時的名稱。
# headers['Content-Disposition'] = 'oss_MultipartUpload.txt'
# 指定該Object的內容編碼格式。
# headers['Content-Encoding'] = 'utf-8'
# 指定到期時間,單位為毫秒。
# headers['Expires'] = '1000'
# 指定初始化分區上傳時是否覆蓋同名Object。此處設定為true,表示禁止覆蓋同名Object。
# headers['x-oss-forbid-overwrite'] = 'true'
# 指定上傳該Object的每個Part時使用的伺服器端加密方式。
# headers[OSS_SERVER_SIDE_ENCRYPTION] = SERVER_SIDE_ENCRYPTION_KMS
# 指定Object的密碼編譯演算法。如果未指定此選項,表明Object使用AES256密碼編譯演算法。
# headers[OSS_SERVER_SIDE_DATA_ENCRYPTION] = SERVER_SIDE_ENCRYPTION_KMS
# 表示KMS託管的使用者主要金鑰。
# headers[OSS_SERVER_SIDE_ENCRYPTION_KEY_ID] = '9468da86-3509-4f8d-a61e-6eab1eac****'
# 指定Object的儲存類型。
# headers['x-oss-storage-class'] = oss2.BUCKET_STORAGE_CLASS_STANDARD
# 指定Object的對象標籤,可同時設定多個標籤。
# headers[OSS_OBJECT_TAGGING] = 'k1=v1&k2=v2&k3=v3'
# upload_id = bucket.init_multipart_upload(key, headers=headers).upload_id
upload_id = bucket.init_multipart_upload(key).upload_id
# 根據upload_id執行取消分區上傳事件或者列舉已上傳分區的操作。
# 如果您需要根據您需要uploadId執行取消分區上傳事件的操作,您需要在調用InitiateMultipartUpload完成初始化分區之後擷取uploadId。
# 如果您需要根據您需要uploadId執行列舉已上傳分區的操作,您需要在調用InitiateMultipartUpload完成初始化分區之後,且在調用CompleteMultipartUpload完成分區上傳之前擷取uploadId。
# print("UploadID:", upload_id)
parts = []

# 逐個上傳分區。
with open(filename, 'rb') as fileobj:
    part_number = 1
    offset = 0
    while offset < total_size:
        num_to_upload = min(part_size, total_size - offset)
        # 調用SizedFileAdapter(fileobj, size)方法會產生一個新的檔案對象,重新計算起始追加位置。
        result = bucket.upload_part(key, upload_id, part_number,
                                    SizedFileAdapter(fileobj, num_to_upload))
        parts.append(PartInfo(part_number, result.etag))

        offset += num_to_upload
        part_number += 1

# 完成分區上傳。
# 如需在完成分區上傳時設定相關Headers,請參考如下範例程式碼。
headers = dict()
# 設定檔案存取權限ACL。此處設定為OBJECT_ACL_PRIVATE,表示私人許可權。
# headers["x-oss-object-acl"] = oss2.OBJECT_ACL_PRIVATE
bucket.complete_multipart_upload(key, upload_id, parts, headers=headers)
# bucket.complete_multipart_upload(key, upload_id, parts)
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Document</title>
  </head>

  <body>
    <button id="submit">上傳</button>
    <input id="file" type="file" />
    <!--匯入sdk檔案-->
    <script
      type="text/javascript"
      src="https://gosspublic.alicdn.com/aliyun-oss-sdk-6.18.0.min.js"
    ></script>
    <script type="text/javascript">
      const client = new OSS({
        // yourRegion填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為oss-cn-hangzhou。
        region: "yourRegion",
        // 從STS服務擷取的臨時存取金鑰(AccessKey ID和AccessKey Secret)。
        accessKeyId: "yourAccessKeyId",
        accessKeySecret: "yourAccessKeySecret",
        // 從STS服務擷取的安全性權杖(SecurityToken)。
        stsToken: "yourSecurityToken",
        // 填寫Bucket名稱,例如examplebucket。
        bucket: "examplebucket",
      });

      const headers = {
        // 指定該Object被下載時的網頁緩衝行為。
        "Cache-Control": "no-cache",
        // 指定該Object被下載時的名稱。
        "Content-Disposition": "example.txt",
        // 指定該Object被下載時的內容編碼格式。
        "Content-Encoding": "utf-8",
        // 指定到期時間,單位為毫秒。
        Expires: "1000",
        // 指定Object的儲存類型。
        "x-oss-storage-class": "Standard",
        // 指定Object標籤,可同時設定多個標籤。
        "x-oss-tagging": "Tag1=1&Tag2=2",
        // 指定初始化分區上傳時是否覆蓋同名Object。此處設定為true,表示禁止覆蓋同名Object。
        "x-oss-forbid-overwrite": "true",
      };

      // 指定上傳到examplebucket的Object名稱,例如exampleobject.txt。
      const name = "exampleobject.txt";
      // 擷取DOM。
      const submit = document.getElementById("submit");
      const options = {
        // 擷取分區上傳進度、斷點和傳回值。
        progress: (p, cpt, res) => {
          console.log(p);
        },
        // 設定並發上傳的分區數量。
        parallel: 4,
        // 設定分區大小。預設值為1 MB,最小值為100 KB。
        partSize: 1024 * 1024,
        // headers,
        // 自訂中繼資料,通過HeadObject介面可以擷取Object的中繼資料。
        meta: { year: 2020, people: "test" },
        mime: "text/plain",
      };

      // 監聽按鈕。
      submit.addEventListener("click", async () => {
        try {
          const data = document.getElementById("file").files[0];
          // 分區上傳。
          const res = await client.multipartUpload(name, data, {
            ...options,
            // 設定上傳回調。
            // 如果不涉及回調伺服器,請刪除callback相關設定。
            callback: {
              // 設定回調請求的伺服器位址。
              url: "http://examplebucket.aliyuncs.com:23450",
              // 設定回調請求訊息頭中Host的值,即您的伺服器配置Host的值。
              host: "yourHost",
              /* eslint no-template-curly-in-string: [0] */
              // 設定發起回調時請求body的值。
              body: "bucket=${bucket}&object=${object}&var1=${x:var1}",
              // 設定發起回調請求的Content-Type。
              contentType: "application/x-www-form-urlencoded",
              customValue: {
                // 設定發起回調請求的自訂參數。
                var1: "value1",
                var2: "value2",
              },
            },
          });
          console.log(res);
        } catch (err) {
          console.log(err);
        }
      });
    </script>
  </body>
</html>
using Aliyun.OSS;

// yourEndpoint填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。
var endpoint = "yourEndpoint";
// 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
var accessKeyId = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_ID");
var accessKeySecret = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_SECRET");
// 填寫Bucket名稱。
var bucketName = "examplebucket";
// 填寫Object完整路徑。Object完整路徑中不能包含Bucket名稱。
var objectName = "exampleobject.txt";
// 填寫本地檔案的完整路徑。如果未指定本地路徑,則預設從樣本程式所屬專案對應本地路徑中上傳檔案。
var localFilename = "D:\\localpath\\examplefile.txt";
// 建立OssClient執行個體。
var client = new OssClient(endpoint, accessKeyId, accessKeySecret);

// 初始化分區上傳,返回uploadId。
var uploadId = "";
try
{
    // 定義上傳的檔案及所屬Bucket的名稱。您可以在InitiateMultipartUploadRequest中設定ObjectMeta,但不必指定其中的ContentLength。
    var request = new InitiateMultipartUploadRequest(bucketName, objectName);
    var result = client.InitiateMultipartUpload(request);
    uploadId = result.UploadId;
    // 列印UploadId。
    Console.WriteLine("Init multi part upload succeeded");
    // 根據uploadId執行取消分區上傳事件或者列舉已上傳分區的操作。
    // 如果您需要根據您需要uploadId執行取消分區上傳事件的操作,您需要在調用InitiateMultipartUpload完成初始化分區之後擷取uploadId。 
    // 如果您需要根據您需要uploadId執行列舉已上傳分區的操作,您需要在調用InitiateMultipartUpload完成初始化分區之後,且在調用CompleteMultipartUpload完成分區上傳之前擷取uploadId。
    Console.WriteLine("Upload Id:{0}", result.UploadId);
}
catch (Exception ex)
{
    Console.WriteLine("Init multi part upload failed, {0}", ex.Message);
    Environment.Exit(1);
}
// 計算分區總數。
var partSize = 100 * 1024;
var fi = new FileInfo(localFilename);
var fileSize = fi.Length;
var partCount = fileSize / partSize;
if (fileSize % partSize != 0)
{
    partCount++;
}
//  當初始化分區成功時,開始分區上傳。PartETags是儲存PartETag的列表,OSS收到使用者提交的分區列表後,會逐一驗證每個分區資料的有效性。當所有的資料分區通過驗證後,OSS會將這些分區組合成一個完整的檔案。
var partETags = new List<PartETag>();
try
{
    using (var fs = File.Open(localFilename, FileMode.Open))
    {
        for (var i = 0; i < partCount; i++)
        {
            var skipBytes = (long)partSize * i;
            // 定位到本次上傳的起始位置。
            fs.Seek(skipBytes, 0);
            // 計算本次上傳的分區大小,最後一片為剩餘的資料大小。
            var size = (partSize < fileSize - skipBytes) ? partSize : (fileSize - skipBytes);
            var request = new UploadPartRequest(bucketName, objectName, uploadId)
            {
                InputStream = fs,
                PartSize = size,
                PartNumber = i + 1
            };
            // 調用UploadPart介面執行上傳功能,返回結果中包含了這個資料片的ETag值。
            var result = client.UploadPart(request);
            partETags.Add(result.PartETag);
            Console.WriteLine("finish {0}/{1}", partETags.Count, partCount);
        }
        Console.WriteLine("Put multi part upload succeeded");
    }
}
catch (Exception ex)
{
    Console.WriteLine("Put multi part upload failed, {0}", ex.Message);
    Environment.Exit(1);
}
// 當分區上傳成功後,合并分區。
try
{
    var completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName, objectName, uploadId);
    foreach (var partETag in partETags)
    {
        completeMultipartUploadRequest.PartETags.Add(partETag);
    }
    var result = client.CompleteMultipartUpload(completeMultipartUploadRequest);
    Console.WriteLine("complete multi part succeeded");
}
catch (Exception ex)
{
    Console.WriteLine("complete multi part failed, {0}", ex.Message);
    Environment.Exit(1);
}
// 填寫Bucket名稱,例如examplebucket。
String bucketName = "examplebucket";
// 填寫Object完整路徑,例如exampledir/exampleobject.txt。Object完整路徑中不能包含Bucket名稱。
String objectName = "exampledir/exampleobject.txt";
// 填寫本地檔案完整路徑,例如/storage/emulated/0/oss/examplefile.txt。
String localFilepath = "/storage/emulated/0/oss/examplefile.txt";

// 初始化分區上傳。
InitiateMultipartUploadRequest init = new InitiateMultipartUploadRequest(bucketName, objectName);
InitiateMultipartUploadResult initResult = oss.initMultipartUpload(init);
// 返回uploadId。
String uploadId = initResult.getUploadId();
// 根據uploadId執行取消分區上傳事件或者列舉已上傳分區的操作。
// 如果您需要根據您需要uploadId執行取消分區上傳事件的操作,您需要在調用InitiateMultipartUpload完成初始化分區之後擷取uploadId。 
// 如果您需要根據您需要uploadId執行列舉已上傳分區的操作,您需要在調用InitiateMultipartUpload完成初始化分區之後,且在調用CompleteMultipartUpload完成分區上傳之前擷取uploadId。
// Log.d("uploadId", uploadId);

// 設定單個Part的大小,單位為位元組,取值範圍為100 KB~5 GB。
int partCount = 100 * 1024;
// 分區上傳。
List<PartETag> partETags = new ArrayList<>();
for (int i = 1; i < 5; i++) {
    byte[] data = new byte[partCount];

    RandomAccessFile raf = new RandomAccessFile(localFilepath, "r");
    long skip = (i-1) * partCount;
    raf.seek(skip);
    raf.readFully(data, 0, partCount);

    UploadPartRequest uploadPart = new UploadPartRequest();
    uploadPart.setBucketName(bucketName);
    uploadPart.setObjectKey(objectName);
    uploadPart.setUploadId(uploadId);
    // 設定分區號,從1開始標識。每一個上傳的Part都有一個分區號,取值範圍是1~10000。
    uploadPart.setPartNumber(i); 
    uploadPart.setPartContent(data);
    try {
        UploadPartResult result = oss.uploadPart(uploadPart);
        PartETag partETag = new PartETag(uploadPart.getPartNumber(), result.getETag());
        partETags.add(partETag);
    } catch (ServiceException serviceException) {
        OSSLog.logError(serviceException.getErrorCode());
    }
}
Collections.sort(partETags, new Comparator<PartETag>() {
    @Override
    public int compare(PartETag lhs, PartETag rhs) {
        if (lhs.getPartNumber() < rhs.getPartNumber()) {
            return -1;
        } else if (lhs.getPartNumber() > rhs.getPartNumber()) {
            return 1;
        } else {
            return 0;
        }
    }
});

// 完成分區上傳。
CompleteMultipartUploadRequest complete = new CompleteMultipartUploadRequest(bucketName, objectName, uploadId, partETags);

// 上傳回調。完成分區上傳請求時可以設定CALLBACK_SERVER參數,請求完成後會向指定的Server Address發送回調請求。可通過返回結果的completeResult.getServerCallbackReturnBody()查看servercallback結果。
complete.setCallbackParam(new HashMap<String, String>() {
    {
        put("callbackUrl", CALLBACK_SERVER); //修改為您的伺服器位址。
        put("callbackBody", "test");
    }
});
CompleteMultipartUploadResult completeResult = oss.completeMultipartUpload(complete);
OSSLog.logError("-------------- serverCallback: " + completeResult.getServerCallbackReturnBody());
package main

import (
	"fmt"
	"log"
	"os"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
)

func main() {
	// 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
	provider, err := oss.NewEnvironmentVariableCredentialsProvider()
	if err != nil {
		log.Fatalf("Error: %v", err)
	}

	// 建立OSSClient執行個體。
	// yourEndpoint填寫Bucket對應的Endpoint,以華東1(杭州)為例,填寫為https://oss-cn-hangzhou.aliyuncs.com。其它Region請按實際情況填寫。
	// yourRegion填寫Bucket所在地區,以華東1(杭州)為例,填寫為cn-hangzhou。其它Region請按實際情況填寫。
	clientOptions := []oss.ClientOption{oss.SetCredentialsProvider(&provider)}
	clientOptions = append(clientOptions, oss.Region("yourRegion"))
	// 設定簽名版本
	clientOptions = append(clientOptions, oss.AuthVersion(oss.AuthV4))
	client, err := oss.New("yourEndpoint", "", "", clientOptions...)
	if err != nil {
		log.Fatalf("Error: %v", err)
	}

	// 設定儲存空間名稱
	bucketName := "examplebucket"
	// 設定Object完整路徑。Object完整路徑中不能包含Bucket名稱。
	objectName := "exampleobject.txt"
	// 設定本地檔案的完整路徑。如果未指定本地路徑,則預設從樣本程式所屬專案對應本地路徑中上傳檔案。
	localFilename := "/localpath/exampleobject.txt"

	bucket, err := client.Bucket(bucketName)
	if err != nil {
		log.Fatalf("Error: %v", err)
	}

	// 設定分區大小(單位:位元組),指定5MB為分區大小。
	partSize := int64(5 * 1024 * 1024)

	// 調用分區上傳函數。
	if err := uploadMultipart(bucket, objectName, localFilename, partSize); err != nil {
		log.Fatalf("Failed to upload multipart: %v", err)
	}

}

// 分區上傳函數
func uploadMultipart(bucket *oss.Bucket, objectName, localFilename string, partSize int64) error {
	// 將本地檔案分區
	chunks, err := oss.SplitFileByPartSize(localFilename, partSize)
	if err != nil {
		return fmt.Errorf("failed to split file into chunks: %w", err)
	}

	// 開啟本地檔案。
	file, err := os.Open(localFilename)
	if err != nil {
		return fmt.Errorf("failed to open file: %w", err)
	}
	defer file.Close()

	// 步驟1:初始化一個分區上傳事件。
	imur, err := bucket.InitiateMultipartUpload(objectName)
	if err != nil {
		return fmt.Errorf("failed to initiate multipart upload: %w", err)
	}

	// 步驟2:上傳分區。
	var parts []oss.UploadPart
	for _, chunk := range chunks {
		part, err := bucket.UploadPart(imur, file, chunk.Size, chunk.Number)
		if err != nil {
			// 如果上傳某個部分失敗,嘗試取消整個上傳任務。
			if abortErr := bucket.AbortMultipartUpload(imur); abortErr != nil {
				log.Printf("Failed to abort multipart upload: %v", abortErr)
			}
			return fmt.Errorf("failed to upload part: %w", err)
		}
		parts = append(parts, part)
	}

	// 指定Object的讀寫權限為私人,預設為繼承Bucket的讀寫權限。
	objectAcl := oss.ObjectACL(oss.ACLPrivate)

	// 步驟3:完成分區上傳。
	_, err = bucket.CompleteMultipartUpload(imur, parts, objectAcl)
	if err != nil {
		// 如果完成上傳失敗,嘗試取消上傳。
		if abortErr := bucket.AbortMultipartUpload(imur); abortErr != nil {
			log.Printf("Failed to abort multipart upload: %v", abortErr)
		}
		return fmt.Errorf("failed to complete multipart upload: %w", err)
	}

	log.Printf("Multipart upload completed successfully.")
	return nil
}
__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];
#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帳號資訊 */
    
    /* yourEndpoint填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。*/
    std::string Endpoint = "yourEndpoint";
    /* yourRegion填寫Bucket所在地區對應的Region。以華東1(杭州)為例,Region填寫為cn-hangzhou。*/
    std::string Region = "yourRegion";
    /* 填寫Bucket名稱,例如examplebucket */
    std::string BucketName = "examplebucket";
    /* 填寫Object完整路徑,完整路徑中不能包含Bucket名稱,例如exampledir/exampleobject.txt。 */
    std::string ObjectName = "exampledir/exampleobject.txt";

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

    ClientConfiguration conf;
    conf.signatureVersion = SignatureVersionType::V4;
    /* 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/
    auto credentialsProvider = std::make_shared<EnvironmentVariableCredentialsProvider>();
    OssClient client(Endpoint, credentialsProvider, conf);
    client.SetRegion(Region);
  
    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;
}
#include "oss_api.h"
#include "aos_http_io.h"
#include <sys/stat.h>
/* yourEndpoint填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。*/
const char *endpoint = "yourEndpoint";

/* 填寫Bucket名稱,例如examplebucket。*/
const char *bucket_name = "examplebucket";
/* 填寫Object完整路徑,完整路徑中不能包含Bucket名稱,例如exampledir/exampleobject.txt。*/
const char *object_name = "exampledir/exampleobject.txt";
/* 填寫本地檔案的完整路徑。*/
const char *local_filename = "yourLocalFilename";
void init_options(oss_request_options_t *options)
{
    options->config = oss_config_create(options->pool);
    /* 用char*類型的字串初始化aos_string_t類型。*/
    aos_str_set(&options->config->endpoint, endpoint);
    /* 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。*/    
    aos_str_set(&options->config->access_key_id, getenv("OSS_ACCESS_KEY_ID"));
    aos_str_set(&options->config->access_key_secret, getenv("OSS_ACCESS_KEY_SECRET"));
    /* 是否使用CNAME訪問OSS服務。0表示不使用。*/
    options->config->is_cname = 0;
    /* 設定網路相關參數,比如逾時時間等。*/
    options->ctl = aos_http_controller_create(options->pool, 0);
}
int64_t get_file_size(const char *file_path)
{
    int64_t filesize = -1;
    struct stat statbuff;
    if(stat(file_path, &statbuff) < 0){
        return filesize;
    } else {
        filesize = statbuff.st_size;
    }
    return filesize;
}
int main(int argc, char *argv[])
{
    /* 在程式入口調用aos_http_io_initialize方法來初始化網路、記憶體等全域資源。*/
    if (aos_http_io_initialize(NULL, 0) != AOSE_OK) {
        exit(1);
    }
    /* 用於記憶體管理的記憶體池(pool),等價於apr_pool_t。其實現代碼在apr庫中。*/
    aos_pool_t *pool;
    /* 重新建立一個記憶體池,第二個參數是NULL,表示沒有繼承其它記憶體池。*/
    aos_pool_create(&pool, NULL);
    /* 建立並初始化options,該參數包括endpoint、access_key_id、acces_key_secret、is_cname、curl等全域配置資訊。*/
    oss_request_options_t *oss_client_options;
    /* 在記憶體池中分配記憶體給options。*/
    oss_client_options = oss_request_options_create(pool);
    /* 初始化Client的選項oss_client_options。*/
    init_options(oss_client_options);
    /* 初始化參數。*/
    aos_string_t bucket;
    aos_string_t object;
    oss_upload_file_t *upload_file = NULL;
    aos_string_t upload_id;   
    aos_table_t *headers = NULL;
    aos_table_t *complete_headers = NULL;
    aos_table_t *resp_headers = NULL;
    aos_status_t *resp_status = NULL; 
    aos_str_set(&bucket, bucket_name);
    aos_str_set(&object, object_name);
    aos_str_null(&upload_id);
    headers = aos_table_make(pool, 1);
    complete_headers = aos_table_make(pool, 1);
    int part_num = 1;
    /* 初始化分區上傳,擷取一個上傳ID(upload_id)。*/
    resp_status = oss_init_multipart_upload(oss_client_options, &bucket, &object, &upload_id, headers, &resp_headers);
    /* 判斷分區上傳初始化是否成功。*/
    if (aos_status_is_ok(resp_status)) {
        printf("Init multipart upload succeeded, upload_id:%.*s\n", 
               upload_id.len, upload_id.data);
    } else {
        printf("Init multipart upload failed, upload_id:%.*s\n", 
               upload_id.len, upload_id.data);
    }
    /* 上傳分區。*/
    int64_t file_length = 0;
    int64_t pos = 0;
    aos_list_t complete_part_list;
       oss_complete_part_content_t* complete_content = NULL;
    char* part_num_str = NULL;
    char* etag = NULL;
    aos_list_init(&complete_part_list);
    file_length = get_file_size(local_filename);
    while(pos < file_length) {
        upload_file = oss_create_upload_file(pool);
        aos_str_set(&upload_file->filename, local_filename);
        upload_file->file_pos = pos;
        pos += 100 * 1024;
        upload_file->file_last = pos < file_length ? pos : file_length;
        resp_status = oss_upload_part_from_file(oss_client_options, &bucket, &object, &upload_id, part_num++, upload_file, &resp_headers);

        /* 儲存分區號和ETag。*/
        complete_content = oss_create_complete_part_content(pool);
        part_num_str = apr_psprintf(pool, "%d", part_num-1);
        aos_str_set(&complete_content->part_number, part_num_str);
        etag = apr_pstrdup(pool,
        (char*)apr_table_get(resp_headers, "ETag"));
        aos_str_set(&complete_content->etag, etag);
        aos_list_add_tail(&complete_content->node, &complete_part_list);

        if (aos_status_is_ok(resp_status)) {
            printf("Multipart upload part from file succeeded\n");
        } else {
            printf("Multipart upload part from file failed\n");
        }
    }

    /* 完成分區上傳。*/
    resp_status = oss_complete_multipart_upload(oss_client_options, &bucket, &object, &upload_id,
            &complete_part_list, complete_headers, &resp_headers);
    /* 判斷分區上傳是否完成。*/
    if (aos_status_is_ok(resp_status)) {
        printf("Complete multipart upload from file succeeded, upload_id:%.*s\n", 
               upload_id.len, upload_id.data);
    } else {
        printf("Complete multipart upload from file failed\n");
    }
    /* 釋放記憶體池,相當於釋放了請求過程中各資源分派的記憶體。*/
    aos_pool_destroy(pool);
    /* 釋放之前分配的全域資源。*/
    aos_http_io_deinitialize();
    return 0;
}

上面所展示的代碼為逐個分區上傳,您也可使用並行上傳,以提高上傳速度,以下為並行上傳的程式碼範例:

import os
from oss2 import determine_part_size, SizedFileAdapter
from oss2.models import PartInfo
import oss2
from concurrent.futures import ThreadPoolExecutor
from oss2.credentials import EnvironmentVariableCredentialsProvider


# 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
auth = oss2.ProviderAuthV4(EnvironmentVariableCredentialsProvider())

# 填寫Bucket所在地區對應的Endpoint。以華東1(杭州)為例,Endpoint填寫為https://oss-cn-hangzhou.aliyuncs.com。
endpoint = "https://oss-cn-hangzhou.aliyuncs.com"

# 填寫Endpoint對應的Region資訊,例如cn-hangzhou。注意,v4簽名下,必須填寫該參數
region = "cn-hangzhou"

# yourBucketName填寫儲存空間名稱。
bucket = oss2.Bucket(auth, endpoint, "yourbucketname", region=region)

# 填寫不能包含Bucket名稱在內的Object完整路徑,例如exampledir/exampleobject.txt。
key = 'exampledir/exampleobject.txt'

# 填寫本地檔案的完整路徑(待上傳檔案)
filename = 'D:\\localpath\\examplefile.txt'


def upload_part(filename, bucket, key, upload_id, part_number, offset, num_to_upload):
    """
    執行單個分區的上傳操作
    :param filename: 本地檔案路徑
    :param bucket: oss2.Bucket 對象,表示OSS儲存空間
    :param key: 上傳檔案的Object名稱
    :param upload_id: 分區上傳的唯一識別碼
    :param part_number: 當前分區的編號
    :param offset: 當前分區的起始位置
    :param num_to_upload: 當前分區的大小
    :return: PartInfo 對象,包含分區的ETag和編號
    """
    with open(filename, 'rb') as fileobj:
        fileobj.seek(offset)
        result = bucket.upload_part(
            key,
            upload_id,
            part_number,
            SizedFileAdapter(fileobj, num_to_upload)
        )
    return PartInfo(part_number, result.etag)



def main():
    """
    主函數,執行檔案上傳流程
    """

    # 擷取待上傳檔案的大小
    total_size = os.path.getsize(filename)
    # 設定分區大小為100KB,可根據需要調整
    part_size = determine_part_size(total_size, preferred_size=100 * 1024)  

    # 初始化分區上傳,返回upload_id
    upload_id = bucket.init_multipart_upload(key).upload_id
    part_info_list = []

    # 建立線程池,設定最大並發數為8,可根據實際情況調整
    max_workers = 8  
    with ThreadPoolExecutor(max_workers=max_workers) as executor:
        futures = []
        part_number = 1
        offset = 0

        # 迴圈上傳所有分區
        while offset < total_size:
            num_to_upload = min(part_size, total_size - offset)
            # 提交上傳任務到線程池
            futures.append(
                executor.submit(upload_part, filename, bucket, key, upload_id, part_number, offset, num_to_upload)
            )
            offset += num_to_upload
            part_number += 1

        # 擷取每個任務的返回結果(PartInfo對象),並將其添加到part_info_list中
        for future in futures:
            part_info_list.append(future.result())

    # 完成分區上傳並返回結果
    bucket.complete_multipart_upload(key, upload_id, part_info_list)
    print(f'Upload completed for {key}')  # 輸出上傳完成的檔案名稱


if __name__ == "__main__":
    main()  # 執行上傳操作

使用命令列工具ossutil

關於使用ossutil分區上傳具體操作, 請參見cp(上傳檔案)

使用REST API

如果您的程式自訂要求較高,您可以直接發起REST API請求。直接發起REST API請求需要手動編寫代碼計算簽名。更多資訊,請參見InitiateMultipartUpload

常見問題

如何刪除片段?

分區上傳過程被中斷後,已上傳的Part會一直儲存在Bucket中。如果您不再需要這些Part,請通過以下方式刪除,以免產生額外的儲存費用。

如何列舉分區?

  • 如果您希望列舉指定Upload ID所屬的所有已經上傳成功Part,請參見ListParts

  • 如果您希望列舉已經初始化但還未完成(Complete)或者還未中止(Abort)的Multipart Upload事件,請參見ListMultipartUploads

分區上傳是否支援上傳本地已加密的壓縮檔?

支援。

分區上傳過程中斷後,重新上傳時是否會覆蓋已上傳Part?

分區上傳過程中斷後,如果使用同一個Upload ID重新上傳所有Part,則會覆蓋之前上傳的同名Part;如果使用新的Upload ID重新上傳所有Part,舊的Upload ID中的分區會作為片段繼續保留。

分區上傳時Upload ID是什麼含義?

Upload ID用於唯一標識分區上傳事件。對於同一個Upload ID,分區號(PartNumber)用於標識該分區在整個檔案內的相對位置。

分區上傳時Upload ID有效期間多久?

Upload ID在分區上傳過程中一直有效,如果上傳終止或上傳完成,Upload ID將失效。如果需要再次分區上傳,您需要重新初始化產生一個新的Upload ID。

OSS是否支援自動合并分區?

OSS不支援自動合并分區,您需要通過調用CompleteMultipartUpload手動合并分區。