全部產品
Search
文件中心

Object Storage Service:斷點續傳上傳

更新時間:May 17, 2025

在上傳大檔案(超過5 GB)到OSS的過程中,如果出現網路中斷、程式異常退出等問題導致檔案上傳失敗,甚至重試多次仍無法完成上傳,您需要使用斷點續傳上傳的方式。斷點續傳上傳將需要上傳的大檔案分成多個較小的分區並發上傳,加速上傳完成時間。如果上傳過程中,某一分區上傳失敗,再次上傳時會從Checkpoint檔案記錄的斷點繼續上傳,無需重新上傳所有分區。上傳完成後,所有分區將合并成完整的檔案。

前提條件

已建立儲存空間(Bucket)。詳情請參見控制台建立儲存空間

注意事項

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

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

  • SDK會將上傳的狀態資訊記錄在Checkpoint檔案中,所以要確保程式對Checkpoint檔案有寫入權限。

  • 請勿修改Checkpoint檔案中攜帶的校正資訊。如果Checkpoint檔案損壞,則會重新上傳所有分區。

  • 如果上傳過程中本地檔案發生了改變,則會重新上傳所有分區。

使用阿里雲SDK

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

import com.aliyun.oss.ClientBuilderConfiguration;
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.common.comm.SignVersion;
import com.aliyun.oss.model.*;

public class UploadFile {
        public static void main(String[] args) throws Exception {
            // Endpoint以華東1(杭州)為例,其它Region請按實際情況填寫。
            String endpoint = "https://oss-cn-hangzhou.aliyuncs.com";
            // 填寫Bucket所在地區。以華東1(杭州)為例,Region填寫為cn-hangzhou。
            String region = "cn-hangzhou";
            // 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
            EnvironmentVariableCredentialsProvider credentialsProvider = CredentialsProviderFactory.newEnvironmentVariableCredentialsProvider();

            // 建立OSSClient執行個體。
            ClientBuilderConfiguration clientBuilderConfiguration = new ClientBuilderConfiguration();
            clientBuilderConfiguration.setSignatureVersion(SignVersion.V4);
            OSS ossClient = OSSClientBuilder.create()
                    .endpoint(endpoint)
                    .credentialsProvider(credentialsProvider)
                    .clientConfiguration(clientBuilderConfiguration)
                    .region(region)
                    .build();

            try {
                ObjectMetadata meta = new ObjectMetadata();
                // 指定上傳的內容類型。
                // meta.setContentType("text/plain");

                // 檔案上傳時設定存取權限ACL。
                // meta.setObjectAcl(CannedAccessControlList.Private);

                // 通過UploadFileRequest設定多個參數。
                // 依次填寫Bucket名稱(例如examplebucket)以及Object完整路徑(例如exampledir/exampleobject.txt),Object完整路徑中不能包含Bucket名稱。
                UploadFileRequest uploadFileRequest = new UploadFileRequest("examplebucket","exampledir/exampleobject.txt");

                // 通過UploadFileRequest設定單個參數。
                // 填寫本地檔案的完整路徑,例如D:\\localpath\\examplefile.txt。如果未指定本地路徑,則預設從樣本程式所屬專案對應本地路徑中上傳檔案。
                uploadFileRequest.setUploadFile("D:\\localpath\\examplefile.txt");
                // 指定上傳並發線程數,預設值為1。
                uploadFileRequest.setTaskNum(5);
                // 指定上傳的分區大小,單位為位元組,取值範圍為100 KB~5 GB。預設值為100 KB。
                uploadFileRequest.setPartSize(1 * 1024 * 1024);
                // 開啟斷點續傳,預設關閉。
                uploadFileRequest.setEnableCheckpoint(true);
                // 記錄本地分區上傳結果的檔案。上傳過程中的進度資訊會儲存在該檔案中,如果某一分區上傳失敗,再次上傳時會根據檔案中記錄的點繼續上傳。上傳完成後,該檔案會被刪除。
                // 如果未設定該值,預設與待上傳的本地檔案同路徑,名稱為${uploadFile}.ucp。
                uploadFileRequest.setCheckpointFile("yourCheckpointFile");
                // 檔案的中繼資料。
                uploadFileRequest.setObjectMetadata(meta);
                // 設定上傳回調,參數為Callback類型。
                //uploadFileRequest.setCallback("yourCallbackEvent");

                // 斷點續傳上傳。
                ossClient.uploadFile(uploadFileRequest);

            } 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 (Throwable 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 {
                // 關閉OSSClient。
                if (ossClient != null) {
                    ossClient.shutdown();
                }
            }
        }
}
const OSS = require('ali-oss')

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名稱。
  bucket: 'examplebucket',
});

// yourfilepath填寫已上傳檔案所在的本地路徑。
const filePath = "yourfilepath";

let checkpoint;
async function resumeUpload() {
  // 重試五次。
  for (let i = 0; i < 5; i++) {
    try {
      const result = await client.multipartUpload('object-name', filePath, {
        checkpoint,
        async progress(percentage, cpt) {
          checkpoint = cpt;
        },
      });
      console.log(result);
      break; // 跳出當前迴圈。
    } catch (e) {
      console.log(e);
    }
  }
}

resumeUpload();
using Aliyun.OSS;
using Aliyun.OSS.Common;

// 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名稱,例如examplebucket。
var bucketName = "examplebucket";
// 填寫Object完整路徑,Object完整路徑中不能包含Bucket名稱,例如exampledir/exampleobject.txt。
var objectName = "exampledir/exampleobject.txt";
// 填寫本地檔案的完整路徑,例如D:\\localpath\\examplefile.txt。
// 如果未指定本地路徑只填寫了檔案名稱(例如examplefile.txt),則預設從樣本程式所屬專案對應本地路徑中上傳檔案。
var localFilename = "D:\\localpath\\examplefile.txt";
// 記錄本地分區上傳結果的檔案。上傳過程中的進度資訊會儲存在該檔案中。
string checkpointDir = "yourCheckpointDir";
// 填寫Bucket所在地區對應的Region。以華東1(杭州)為例,Region填寫為cn-hangzhou。
const string region = "cn-hangzhou";

// 建立ClientConfiguration執行個體,按照您的需要修改預設參數。
var conf = new ClientConfiguration();

// 設定v4簽名。
conf.SignatureVersion = SignatureVersion.V4;

// 建立OssClient執行個體。
var client = new OssClient(endpoint, accessKeyId, accessKeySecret, conf);
client.SetRegion(region);
try
{
    // 通過UploadFileRequest設定多個參數。
    UploadObjectRequest request = new UploadObjectRequest(bucketName, objectName, localFilename)
    {
        // 指定上傳的分區大小。
        PartSize = 8 * 1024 * 1024,
        // 指定並發線程數。
        ParallelThreadCount = 3,
        // checkpointDir儲存斷點續傳的中間狀態,用於失敗後繼續上傳。
        // 如果checkpointDir為null,斷點續傳功能不會生效,每次失敗後都會重新上傳。
        CheckpointDir = checkpointDir,
    };
    // 斷點續傳上傳。
    client.ResumableUploadObject(request);
    Console.WriteLine("Resumable upload object:{0} succeeded", objectName);
}
catch (OssException ex)
{
    Console.WriteLine("Failed with error code: {0}; Error info: {1}. \nRequestID:{2}\tHostID:{3}",
        ex.ErrorCode, ex.Message, ex.RequestId, ex.HostId);
}
catch (Exception ex)
{
    Console.WriteLine("Failed with error info: {0}", ex.Message);
}
// 擷取UploadId上傳檔案。
OSSResumableUploadRequest * resumableUpload = [OSSResumableUploadRequest new];
resumableUpload.bucketName = <bucketName>;
// objectKey等同於objectName,表示斷點上傳檔案到OSS時需要指定包含檔案尾碼在內的完整路徑,例如abc/efg/123.jpg
resumableUpload.objectKey = <objectKey>;
resumableUpload.partSize = 1024 * 1024;
resumableUpload.uploadProgress = ^(int64_t bytesSent, int64_t totalByteSent, int64_t totalBytesExpectedToSend) {
    NSLog(@"%lld, %lld, %lld", bytesSent, totalByteSent, totalBytesExpectedToSend);
};
NSString *cachesDir = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) firstObject];
// 設定斷點記錄儲存路徑。
resumableUpload.recordDirectoryPath = cachesDir;
// 將參數deleteUploadIdOnCancelling設定為NO,表示不刪除斷點記錄檔案,上傳失敗後將從斷點記錄處繼續上傳直到檔案上傳完成。如果不設定此參數,即保留預設值YES,表示刪除斷點記錄檔案,下次再上傳同一檔案時則重新上傳。
resumableUpload.deleteUploadIdOnCancelling = NO;

resumableUpload.uploadingFileURL = [NSURL fileURLWithPath:<your file path>];
OSSTask * resumeTask = [client resumableUpload:resumableUpload];
[resumeTask continueWithBlock:^id(OSSTask *task) {
    if (task.error) {
        NSLog(@"error: %@", task.error);
        if ([task.error.domain isEqualToString:OSSClientErrorDomain] && task.error.code == OSSClientErrorCodeCannotResumeUpload) {
            // 此任務無法續傳,需擷取新的uploadId重新上傳。
        }
    } else {
        NSLog(@"Upload file success");
    }
    return nil;
}];

// [resumeTask waitUntilFinished];

// [resumableUpload cancel];
                    
#include <alibabacloud/oss/OssClient.h>
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";
    /* 填寫本地檔案的完整路徑,例如D:\\localpath\\examplefile.txt。如果未指定本地路徑,則預設從樣本程式所屬專案對應本地路徑中上傳檔案。*/
    std::string UploadFilePath = "D:\\localpath\\examplefile.txt";
    /* 記錄本地分區上傳結果的檔案。上傳過程中的進度資訊會儲存在該檔案中,如果某一分區上傳失敗,再次上傳時會根據檔案中記錄的斷點繼續上傳。上傳完成後,該檔案會被刪除。*/
    /* 設定斷點記錄檔案所在的目錄,並確保指定的目錄已存在,例如D:\\local。如果未設定該值,則不會記錄斷點續傳資訊,不會使用斷點續傳。*/
    std::string CheckpointFilePath = "D:\\local";

    /* 初始化網路等資源。*/
    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);

    /* 斷點續傳上傳。*/
    UploadObjectRequest request(BucketName, ObjectName, UploadFilePath, CheckpointFilePath);
    auto outcome = client.ResumableUploadObject(request);

    if (!outcome.isSuccess()) {
        /* 異常處理。*/
        std::cout << "ResumableUploadObject 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"
/* 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";
/* yourRegion填寫Bucket所在地區對應的Region。以華東1(杭州)為例,Region填寫為cn-hangzhou。*/
const char *region = "yourRegion";
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"));
    //需要額外配置以下兩個參數
    aos_str_set(&options->config->region, region);
    options->config->signature_version = 4;
    /* 是否使用了CNAME。0表示不使用。*/
    options->config->is_cname = 0;
    /* 設定網路相關參數,比如逾時時間等。*/
    options->ctl = aos_http_controller_create(options->pool, 0);
}
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;
    aos_string_t file;
    aos_list_t resp_body;
    aos_table_t *headers = NULL;
    aos_table_t *resp_headers = NULL; 
    aos_status_t *resp_status = NULL; 
    oss_resumable_clt_params_t *clt_params;
    aos_str_set(&bucket, bucket_name);
    aos_str_set(&object, object_name);
    aos_str_set(&file, local_filename);
    aos_list_init(&resp_body);
    /* 斷點續傳。*/
    clt_params = oss_create_resumable_clt_params_content(pool, 1024 * 100, 3, AOS_TRUE, NULL);
    resp_status = oss_resumable_upload_file(oss_client_options, &bucket, &object, &file, headers, NULL, clt_params, NULL, &resp_headers, &resp_body);
    if (aos_status_is_ok(resp_status)) {
        printf("resumable upload succeeded\n");
    } else {
        printf("resumable upload failed\n");
    }
    /* 釋放記憶體池,相當於釋放了請求過程中各資源分派的記憶體。*/
    aos_pool_destroy(pool);
    /* 釋放之前分配的全域資源。*/
    aos_http_io_deinitialize();
    return 0;
}
import argparse
import alibabacloud_oss_v2 as oss

# 建立一個命令列參數解析器,並描述指令碼用途:上傳檔案樣本
parser = argparse.ArgumentParser(description="upload file sample")

# 添加命令列參數 --region,表示儲存空間所在的地區,必需參數
parser.add_argument('--region', help='The region in which the bucket is located.', required=True)
# 添加命令列參數 --bucket,表示要上傳檔案到的儲存空間名稱,必需參數
parser.add_argument('--bucket', help='The name of the bucket.', required=True)
# 添加命令列參數 --endpoint,表示其他服務可用來訪問OSS的網域名稱,非必需參數
parser.add_argument('--endpoint', help='The domain names that other services can use to access OSS')
# 添加命令列參數 --key,表示對象(檔案)在OSS中的鍵名,必需參數
parser.add_argument('--key', help='The name of the object.', required=True)
# 添加命令列參數 --file_path,表示本地待上傳檔案的路徑,必需參數,例如“/Users/yourLocalPath/yourFileName”
parser.add_argument('--file_path', help='The path of Upload file.', required=True)

def main():
    # 解析命令列提供的參數,擷取使用者輸入的值
    args = parser.parse_args()

    # 從環境變數中載入訪問OSS所需的認證資訊,用於身分識別驗證
    credentials_provider = oss.credentials.EnvironmentVariableCredentialsProvider()

    # 使用SDK的預設配置建立設定物件,並設定認證提供者
    cfg = oss.config.load_default()
    cfg.credentials_provider = credentials_provider
    
    # 設定設定物件的地區屬性,根據使用者提供的命令列參數
    cfg.region = args.region

    # 如果提供了自訂endpoint,則更新設定物件中的endpoint屬性
    if args.endpoint is not None:
        cfg.endpoint = args.endpoint

    # 使用上述配置初始化OSS用戶端,準備與OSS互動
    client = oss.Client(cfg)

    # 建立一個用於上傳檔案的對象,並開啟斷點續傳功能,指定斷點記錄檔案的儲存路徑
    uploader = client.uploader(enable_checkpoint=True, checkpoint_dir="/Users/yourLocalPath/checkpoint/")

    # 調用方法執行檔案上傳操作
    result = uploader.upload_file(
        oss.PutObjectRequest(
            bucket=args.bucket,  # 指定目標儲存空間
            key=args.key,        # 指定檔案在OSS中的名稱
        ),
        filepath=args.file_path  # 指定本地檔案的位置
    )

    # 列印上傳結果的相關資訊,包括狀態代碼、請求ID、內容MD5等
    print(f'status code: {result.status_code},'
          f' request id: {result.request_id},'
          f' content md5: {result.headers.get("Content-MD5")},'
          f' etag: {result.etag},'
          f' hash crc64: {result.hash_crc64},'
          f' version id: {result.version_id},'
          f' server time: {result.headers.get("x-oss-server-time")},'
          )

# 當此指令碼被直接執行時,調用main函數開始處理邏輯
if __name__ == "__main__":
    main()  # 指令碼進入點,控製程序流程從這裡開始
<?php

// 引入自動負載檔案,確保依賴庫能夠正確載入
require_once __DIR__ . '/../vendor/autoload.php';

use AlibabaCloud\Oss\V2 as Oss;

// 定義命令列參數的描述資訊
$optsdesc = [
    "region" => ['help' => 'The region in which the bucket is located.', 'required' => True], // Bucket所在的地區(必填)
    "endpoint" => ['help' => 'The domain names that other services can use to access OSS.', 'required' => False], // 訪問網域名稱(可選)
    "bucket" => ['help' => 'The name of the bucket', 'required' => True], // Bucket名稱(必填)
    "key" => ['help' => 'The name of the object', 'required' => True], // 對象名稱(必填)
];

// 將參數描述轉換為getopt所需的長選項格式
// 每個參數後面加上":"表示該參數需要值
$longopts = \array_map(function ($key) {
    return "$key:";
}, array_keys($optsdesc));

// 解析命令列參數
$options = getopt("", $longopts);

// 驗證必填參數是否存在
foreach ($optsdesc as $key => $value) {
    if ($value['required'] === True && empty($options[$key])) {
        $help = $value['help']; // 擷取參數的協助資訊
        echo "Error: the following arguments are required: --$key, $help" . PHP_EOL;
        exit(1); // 如果必填參數缺失,則退出程式
    }
}

// 從解析的參數中提取值
$region = $options["region"]; // Bucket所在的地區
$bucket = $options["bucket"]; // Bucket名稱
$key = $options["key"];       // 對象名稱

// 載入環境變數中的憑證資訊
// 使用EnvironmentVariableCredentialsProvider從環境變數中讀取Access Key ID和Access Key Secret
$credentialsProvider = new Oss\Credentials\EnvironmentVariableCredentialsProvider();

// 使用SDK的預設配置
$cfg = Oss\Config::loadDefault();
$cfg->setCredentialsProvider($credentialsProvider); // 設定憑證提供者
$cfg->setRegion($region); // 設定Bucket所在的地區
if (isset($options["endpoint"])) {
    $cfg->setEndpoint($options["endpoint"]); // 如果提供了訪問網域名稱,則設定endpoint
}

// 建立OSS用戶端執行個體
$client = new Oss\Client($cfg);

// 定義要上傳的本地檔案路徑
$filename = "/Users/yourLocalPath/yourFileName"; // 樣本檔案路徑

// 建立上傳器執行個體
$uploader = $client->newUploader();

// 執行分區上傳操作
$result = $uploader->uploadFile(
    request: new Oss\Models\PutObjectRequest(bucket: $bucket, key: $key), // 建立PutObjectRequest對象,指定Bucket和對象名稱
    filepath: $filename, // 指定要上傳的本地檔案路徑
);

// 列印分區上傳結果
printf(
    'multipart upload status code:' . $result->statusCode . PHP_EOL . // HTTP狀態代碼,例如200表示成功
    'multipart upload request id:' . $result->requestId . PHP_EOL .   // 請求ID,用於調試或追蹤請求
    'multipart upload result:' . var_export($result, true) . PHP_EOL  // 分區上傳的詳細結果
);
package main

import (
	"context"
	"flag"
	"log"

	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
	"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
)

// 定義全域變數
var (
	region     string // 儲存地區
	bucketName string // 儲存空間名稱
	objectName string // 對象名稱
)

// init函數用於初始化命令列參數
func init() {
	flag.StringVar(&region, "region", "", "The region in which the bucket is located.")
	flag.StringVar(&bucketName, "bucket", "", "The name of the bucket.")
	flag.StringVar(&objectName, "object", "", "The name of the source object.")
}

func main() {
	// 解析命令列參數
	flag.Parse()

	// 檢查bucket名稱是否為空白
	if len(bucketName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, bucket name required")
	}

	// 檢查region是否為空白
	if len(region) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, region required")
	}

	// 檢查object名稱是否為空白
	if len(objectName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, source object name required")
	}

	// 載入預設配置並設定憑證提供者和地區
	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	// 建立OSS用戶端
	client := oss.NewClient(cfg)

	// 建立上傳器,並啟用斷點續傳功能
	u := client.NewUploader(func(uo *oss.UploaderOptions) {
		uo.CheckpointDir = "/Users/yourLocalPath/checkpoint/" // 指定斷點記錄檔案的儲存路徑
		uo.EnableCheckpoint = true        // 開啟斷點續傳
	})

	// 定義本地檔案路徑,需要替換為您的實際本地檔案路徑和檔案名稱
	localFile := "/Users/yourLocalPath/yourFileName"

	// 執行上傳檔案的操作
	result, err := u.UploadFile(context.TODO(),
		&oss.PutObjectRequest{
			Bucket: oss.Ptr(bucketName),
			Key:    oss.Ptr(objectName)},
		localFile)
	if err != nil {
		log.Fatalf("failed to upload file %v", err)
	}

	// 列印上傳檔案的結果
	log.Printf("upload file result:%#v\n", result)
}