Object Storage Service (OSS) は、ラージオブジェクトを複数のパートに分けてアップロードできるマルチパートアップロード機能を提供します。すべてのパートがアップロードされた後、CompleteMultipartUpload 操作を呼び出して、それらを完全なオブジェクトに結合できます。
注意事項
このトピックのサンプルコードでは、中国 (杭州) リージョンのリージョン ID
cn-hangzhouを使用します。デフォルトでは、パブリックエンドポイントを使用してバケット内のリソースにアクセスします。同じリージョン内の他の Alibaba Cloud サービスからバケットリソースにアクセスする場合は、内部エンドポイントを使用します。OSS のリージョンとエンドポイントの詳細については、「リージョンとエンドポイント」をご参照ください。このトピックでは、アクセス資格情報は環境変数から取得されます。アクセス資格情報の設定方法の詳細については、「アクセス資格情報の設定」をご参照ください。
マルチパートアップロードを使用するには、
oss:PutObject権限が必要です。詳細については、「RAM ユーザーへのカスタムポリシーのアタッチ」をご参照ください。
マルチパートアップロードのプロセス
マルチパートアップロードには、次の 3 つのステップが含まれます。
マルチパートアップロードイベントを開始します。
Client.InitiateMultipartUpload メソッドを呼び出して、OSS によって作成されたグローバルに一意のアップロード ID を取得します。
パートをアップロードします。
Client.UploadPart メソッドを呼び出して、パートデータをアップロードします。
説明特定のアップロード ID に対して、パート番号は完全なオブジェクト内でのパートの位置を識別します。同じパート番号を使用して新しいデータをアップロードすると、OSS 内のパートの既存データは上書きされます。
OSS は、受信したパートデータの MD5 ハッシュを、ユーザーに返される ETag ヘッダーに含めます。
OSS は、アップロードされたデータの MD5 ハッシュを計算し、SDK によって計算された MD5 ハッシュと比較します。2 つの MD5 ハッシュが異なる場合、InvalidDigest エラーコードが返されます。
マルチパートアップロードを完了します。
すべてのパートがアップロードされた後、Client.CompleteMultipartUpload メソッドを呼び出して、パートを完全なオブジェクトに結合します。
例
次のサンプルコードは、大きなローカルファイルを複数のパートに分割し、それらのパートをバケットに同時にアップロードしてから、パートを完全なオブジェクトに結合する方法を示しています。
package main
import (
"bufio"
"bytes"
"context"
"flag"
"io"
"log"
"os"
"sync"
"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 source bucket.")
flag.StringVar(&objectName, "object", "", "The name of the source object.")
}
func main() {
// コマンドラインパラメーターを解析します。
flag.Parse()
// アップロード ID を定義します。
var uploadId string
// ソースバケット名が空かどうかを確認します。
if len(bucketName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, source 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, source object name required")
}
// デフォルトの構成をロードし、資格情報プロバイダーとリージョンを設定します。
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
// OSS クライアントを作成します。
client := oss.NewClient(cfg)
// マルチパートアップロードリクエストを開始します。
initRequest := &oss.InitiateMultipartUploadRequest{
Bucket: oss.Ptr(bucketName),
Key: oss.Ptr(objectName),
}
initResult, err := client.InitiateMultipartUpload(context.TODO(), initRequest)
if err != nil {
log.Fatalf("failed to initiate multipart upload %v", err)
}
// マルチパートアップロードの開始結果を出力します。
log.Printf("initiate multipart upload result:%#v\n", *initResult.UploadId)
uploadId = *initResult.UploadId
// wait group と mutex を初期化します。
var wg sync.WaitGroup
var parts []oss.UploadPart
count := 3
var mu sync.Mutex
// ローカルファイルの内容をメモリに読み込みます。yourLocalFile を実際のローカルファイル名とパスに置き換えます。
file, err := os.Open("yourLocalFile")
if err != nil {
log.Fatalf("failed to open local file %v", err)
}
defer file.Close()
bufReader := bufio.NewReader(file)
content, err := io.ReadAll(bufReader)
if err != nil {
log.Fatalf("failed to read local file %v", err)
}
log.Printf("file size: %d\n", len(content))
// 各パートのサイズを計算します。
chunkSize := len(content) / count
if chunkSize == 0 {
chunkSize = 1
}
// 複数のゴルーチンを開始してパートをアップロードします。
for i := 0; i < count; i++ {
start := i * chunkSize
end := start + chunkSize
if i == count-1 {
end = len(content)
}
wg.Add(1)
go func(partNumber int, start, end int) {
defer wg.Done()
// パートをアップロードするリクエストを作成します。
partRequest := &oss.UploadPartRequest{
Bucket: oss.Ptr(bucketName), // 宛先バケットの名前。
Key: oss.Ptr(objectName), // 宛先オブジェクトの名前。
PartNumber: int32(partNumber), // パート番号。
UploadId: oss.Ptr(uploadId), // アップロード ID。
Body: bytes.NewReader(content[start:end]), // パートの内容。
}
// パートをアップロードするリクエストを送信します。
partResult, err := client.UploadPart(context.TODO(), partRequest)
if err != nil {
log.Fatalf("failed to upload part %d: %v", partNumber, err)
}
// パートアップロードの結果を記録します。
part := oss.UploadPart{
PartNumber: partRequest.PartNumber,
ETag: partResult.ETag,
}
// mutex を使用して共有データを保護します。
mu.Lock()
parts = append(parts, part)
mu.Unlock()
}(i+1, start, end)
}
// すべてのゴルーチンが完了するまで待ちます。
wg.Wait()
// マルチパートアップロードリクエストを完了します。
request := &oss.CompleteMultipartUploadRequest{
Bucket: oss.Ptr(bucketName),
Key: oss.Ptr(objectName),
UploadId: oss.Ptr(uploadId),
CompleteMultipartUpload: &oss.CompleteMultipartUpload{
Parts: parts,
},
}
result, err := client.CompleteMultipartUpload(context.TODO(), request)
if err != nil {
log.Fatalf("failed to complete multipart upload %v", err)
}
// マルチパートアップロードの完了結果を出力します。
log.Printf("complete multipart upload result:%#v\n", result)
}
一般的なシナリオ
関連情報
マルチパートアップロードの完全なサンプルコードについては、「GitHub の例」をご参照ください。
完全なマルチパートアップロードの実装には、次の 3 つの API 操作が含まれます:
マルチパートアップロードイベントを開始するには、「InitiateMultipartUpload」をご参照ください。
パートをアップロードするには、「UploadPart」をご参照ください。
マルチパートアップロードを完了するには、「CompleteMultipartUpload」をご参照ください。
マルチパートアップロードイベントをキャンセルするには、「AbortMultipartUpload」をご参照ください。
アップロードされたパートをリストするには、「NewListPartsPaginator」をご参照ください。
進行中のすべてのマルチパートアップロードイベント (開始されたが完了またはキャンセルされていないイベント) をリストするには、「NewListMultipartUploadsPaginator」をご参照ください。