全部產品
Search
文件中心

Object Storage Service:追加上傳(Go SDK V2)

更新時間:Aug 02, 2025

追加上傳是指在已上傳的追加類型檔案(Appendable Object)末尾直接追加內容。本文介紹如何使用OSS Go SDK進行追加上傳。

注意事項

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

  • 本文以從環境變數讀取存取憑證為例。如何配置訪問憑證,請參見配置訪問憑證

  • 當檔案不存在時,調用追加上傳介面會建立一個追加類型檔案。

  • 當檔案已存在時:

    • 如果檔案為追加類型檔案,且設定的追加位置和檔案當前長度相等,則直接在該檔案末尾追加內容。

    • 如果檔案為追加類型檔案,但是設定的追加位置和檔案當前長度不相等,則拋出PositionNotEqualToLength異常。

    • 如果檔案為非追加類型檔案,例如通過簡單上傳的檔案類型為Normal的檔案,則拋出ObjectNotAppendable異常。

許可權說明

阿里雲帳號預設擁有全部許可權。阿里雲帳號下的RAM使用者或RAM角色預設沒有任何許可權,需要阿里雲帳號或帳號管理員通過RAM PolicyBucket Policy授予操作許可權。

API

Action

說明

AppendObject

oss:PutObject

以追加寫的方式上傳檔案(Object)。

oss:PutObjectTagging

以追加寫的方式上傳檔案(Object)時,如果通過x-oss-tagging指定Object的標籤,則需要此操作的許可權。

方法定義

針對檔案追加上傳的情境,Go SDK V2新增了AppendFile介面以模仿檔案的讀寫行為,用於操作儲存空間裡的對象,以下列舉了AppendFile與AppendObject介面的具體說明:

介面名

說明

Client.AppendObject

追加上傳, 最終檔案最大支援5GiB

支援CRC64資料校正(預設啟用)

支援進度條

請求body類型為io.Reader, 當支援io.Seeker類型時,具備失敗重傳(該介面為非等冪介面,重傳時可能出現失敗)

Client.AppendFile

與Client.AppendObject介面能力一致

最佳化了重傳時失敗後容錯處理

包含AppendOnlyFile介面

AppendOnlyFile.Write

AppendOnlyFile.WriteFrom

進階版追加上傳API:AppendFile

調用AppendFile介面以追加寫的方式上傳資料。如果對象不存在,則建立追加類型的對象。如果對象存在,並且不為追加類型的對象,則返回錯誤。

AppendFile介面定義如下。

type AppendOnlyFile struct {
...
}

func (c *Client) AppendFile(ctx context.Context, bucket string, key string, optFns ...func(*AppendOptions)) (*AppendOnlyFile, error)

請求參數列表

參數名

類型

說明

ctx

context.Context

請求的上下文

bucket

string

設定儲存空間名

key

string

設定對象名

optFns

...func(*AppendOptions)

(可選)追加檔案時的配置選項

其中,AppendOptions的參數說明列舉如下:

參數

類型

說明

RequestPayer

*string

啟用了要求者付費模式時,需要設定為'requester'

CreateParameter

*AppendObjectRequest

用於首次上傳時,設定對象的元資訊,包括ContentType,Metadata,許可權,儲存類型等,具體請參見AppendObjectRequest

傳回值列表

傳回值名

類型

說明

file

*AppendOnlyFile

追加檔案的執行個體,當 err 為nil 時有效,具體請參見AppendOnlyFile

err

error

開啟追加檔案時的狀態,當失敗時,err 不為 nil

其中,AppendOnlyFile介面說明如下:

介面名

說明

Close() error

關閉檔案控制代碼,釋放資源

Write(b []byte) (int, error)

將b中的資料寫入到資料流中,返回寫入的位元組數和遇到的錯誤

WriteFrom(r io.Reader) (int64, error)

將r中的資料寫入到資料流中,返回寫入的位元組數和遇到的錯誤

Stat() (os.FileInfo, error)

擷取對象的資訊,包括對象大小、最後修改時間以及元資訊

基礎版追加上傳API:AppendObject

func (c *Client) AppendObject(ctx context.Context, request *AppendObjectRequest, optFns ...func(*Options)) (*AppendObjectResult, error)

請求參數列表

參數名

類型

說明

ctx

context.Context

請求的上下文,可以用來佈建要求的總時限

request

*AppendObjectRequest

設定具體介面的請求參數,具體請參見AppendObjectRequest

optFns

...func(*Options)

(可選)介面級的配置參數, 具體請參見Options

傳回值列表

傳回值名

類型

說明

result

*AppendObjectResult

介面傳回值,當 err 為nil 時有效,具體請參見AppendObjectResult

err

error

請求的狀態,當請求失敗時,err 不為 nil

範例程式碼

使用AppendFile追加上傳

package main

import (
	"context"
	"flag"
	"log"
	"os"

	"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 object.")
}

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

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

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

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

	// 配置OSS用戶端
	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

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

	// 建立一個追加檔案的執行個體
	f, err := client.AppendFile(context.TODO(),
		bucketName,
		objectName,
		func(ao *oss.AppendOptions) {
			ao.CreateParameter = &oss.AppendObjectRequest{
				Acl: oss.ObjectACLPrivate, // 設定對象的存取權限為私人
				Metadata: map[string]string{
					"user": "jack", // 設定對象的中繼資料
				},
				Tagging: oss.Ptr("key=value"), // 設定對象的標籤
			}
		})

	if err != nil {
		log.Fatalf("failed to append file %v", err)
	}
	defer f.Close() // 確保檔案流在函數結束時關閉

	// 開啟本地檔案 example1.txt
	lf, err := os.Open("/local/dir/example1.txt")
	if err != nil {
		log.Fatalf("failed to open local file %v", err)
	}

	// 將 example1.txt 的內容追加到 OSS 對象
	_, err = f.WriteFrom(lf)
	if err != nil {
		log.Fatalf("failed to append file %v", err)
	}
	lf.Close() // 關閉本地檔案

	// 開啟本地檔案 example2.txt
	lf, err = os.Open("/local/dir/example2.txt")
	if err != nil {
		log.Fatalf("failed to open local file %v", err)
	}

	// 將 example2.txt 的內容追加到 OSS 對象
	_, err = f.WriteFrom(lf)
	if err != nil {
		log.Fatalf("failed to append file %v", err)
	}
	lf.Close() // 關閉本地檔案

	// 讀取本地檔案 example3.txt 的內容
	lb, err := os.ReadFile("/local/dir/example3.txt")
	if err != nil {
		log.Fatalf("failed to read local file %v", err)
	}

	// 將 example3.txt 的內容追加到 OSS 對象
	_, err = f.Write(lb)
	if err != nil {
		log.Fatalf("failed to append file %v", err)
	}

	// 列印成功追加檔案的日誌資訊
	log.Printf("append file successfully")
}

使用AppendObject追加上傳

package main

import (
	"context"
	"flag"
	"log"
	"strings"

	"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 object.")
}

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

	// 定義追加檔案的初始位置
	var (
		position = int64(0)
	)

	// 檢查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, object name required")
	}

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

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

	// 定義要追加的內容
	content := "hi append object"

	// 建立AppendObject請求
	request := &oss.AppendObjectRequest{
		Bucket:   oss.Ptr(bucketName),
		Key:      oss.Ptr(objectName),
		Position: oss.Ptr(position),
		Body:     strings.NewReader(content),
	}

	// 執行AppendObject請求並處理結果
	// 第一次追加上傳的位置是0,傳回值中包含下一次追加的位置
	result, err := client.AppendObject(context.TODO(), request)
	if err != nil {
		log.Fatalf("failed to append object %v", err)
	}

	// 建立第二次AppendObject請求
	request = &oss.AppendObjectRequest{
		Bucket:   oss.Ptr(bucketName),
		Key:      oss.Ptr(objectName),
		Position: oss.Ptr(result.NextPosition), //從第一次AppendObject傳回值中擷取NextPosition
		Body:     strings.NewReader("hi append object"),
	}

	// 執行第二次AppendObject請求並處理結果
	result, err = client.AppendObject(context.TODO(), request)
	if err != nil {
		log.Fatalf("failed to append object %v", err)
	}

	log.Printf("append object result:%#v\n", result)
}

常見使用情境

追加上傳並顯示進度條

package main

import (
	"context"
	"flag"
	"fmt"
	"log"
	"strings"

	"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 object.")
}

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

	// 定義追加檔案的初始位置
	var (
		position = int64(0)
	)

	// 檢查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, object name required")
	}

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

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

	// 定義要追加的內容
	content := "hi append object"

	// 建立AppendObject請求
	request := &oss.AppendObjectRequest{
		Bucket:   oss.Ptr(bucketName),
		Key:      oss.Ptr(objectName),
		Position: oss.Ptr(position),
		Body:     strings.NewReader(content),
		ProgressFn: func(increment, transferred, total int64) {
			fmt.Printf("increment:%v, transferred:%v, total:%v\n", increment, transferred, total)
		}, // 進度回呼函數,用於顯示上傳進度
	}

	// 執行第一次AppendObject請求並處理結果
	// 第一次追加上傳的位置是0,傳回值中包含下一次追加的位置
	result, err := client.AppendObject(context.TODO(), request)
	if err != nil {
		log.Fatalf("failed to append object %v", err)
	}

	// 建立第二次AppendObject請求
	request = &oss.AppendObjectRequest{
		Bucket:   oss.Ptr(bucketName),
		Key:      oss.Ptr(objectName),
		Position: oss.Ptr(result.NextPosition), //從第一次AppendObject傳回值中擷取NextPosition
		Body:     strings.NewReader("hi append object"),
		ProgressFn: func(increment, transferred, total int64) {
			fmt.Printf("increment:%v, transferred:%v, total:%v\n", increment, transferred, total)
		}, // 進度回呼函數,用於顯示上傳進度
	}

	// 執行第二次AppendObject請求並處理結果
	result, err = client.AppendObject(context.TODO(), request)
	if err != nil {
		log.Fatalf("failed to append object %v", err)
	}

	// 列印追加對象的versionId
	log.Printf("append object result:%#v\n", *result.VersionId)
}

相關文檔