追加上傳是指在已上傳的追加類型文件(Appendable Object)末尾直接追加內容。本文介紹如何使用OSS Go SDK進行追加上傳。
注意事項
本文示例代碼以華東1(杭州)的地域ID
cn-hangzhou
為例,默認使用外網Endpoint,如果您希望通過與OSS同地域的其他阿里云產品訪問OSS,請使用內網Endpoint。關于OSS支持的Region與Endpoint的對應關系,請參見OSS地域和訪問域名。本文以從環境變量讀取訪問憑證為例。如何配置訪問憑證,請參見配置訪問憑證。
要進行簡單上傳,您必須有
oss:PutObject
權限。具體操作,請參見為RAM用戶授權自定義的權限策略。當文件不存在時,調用追加上傳接口會創建一個追加類型文件。
當文件已存在時:
如果文件為追加類型文件,且設置的追加位置和文件當前長度相等,則直接在該文件末尾追加內容。
如果文件為追加類型文件,但是設置的追加位置和文件當前長度不相等,則拋出PositionNotEqualToLength異常。
如果文件為非追加類型文件,例如通過簡單上傳的文件類型為Normal的文件,則拋出ObjectNotAppendable異常。
方法定義
針對文件追加上傳的場景,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 |
示例代碼
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(®ion, "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")
}
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(®ion, "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)
}
相關文檔
關于追加上傳的高級API接口,請參見AppendFile
關于追加上傳的基礎API接口,請參見AppendObject。