すべてのプロダクト
Search
ドキュメントセンター

Object Storage Service:マルチパートアップロード (Go SDK V2)

最終更新日:Mar 21, 2026

マルチパートアップロードは、大きなオブジェクトを複数のパートに分割し、それぞれを独立してアップロードします。すべてのパートがアップロードされた後、CompleteMultipartUpload を呼び出して、それらを完全なオブジェクトにアセンブルします。

前提条件

開始する前に、以下が準備できていることを確認してください。

仕組み

マルチパートアップロードは、3 つのステップで構成されます。

  1. 開始Client.InitiateMultipartUpload を呼び出して、OSS から一意のアップロード ID を取得します。

  2. パートのアップロードClient.UploadPart を呼び出して、そのアップロード ID を使用して各パートをアップロードします。

  3. 完了Client.CompleteMultipartUpload を呼び出して、アップロードされたすべてのパートを最終的なオブジェクトにアセンブルします。

パート番号の動作:

  • パート番号は、最終的なオブジェクトにおけるパートの順序を定義します。既存のパート番号で新しいパートをアップロードすると、元のパートが上書きされます。

  • OSS は、アップロードされた各パートの MD5 ハッシュを ETag レスポンスヘッダーで返します。その後、OSS SDK for Go によって計算されたハッシュと検証します。これらが異なる場合、OSS は InvalidDigest を返します。詳細については、「ETag 値を OSS MD5 ハッシュとして使用してデータ整合性を確認できますか」をご参照ください。

サンプルコードでは、リージョン cn-hangzhou とパブリックエンドポイントを使用しています。同じリージョン内の別の Alibaba Cloud サービスから OSS にアクセスするには、内部エンドポイントに切り替えてください。利用可能なリージョンとエンドポイントについては、「リージョンとエンドポイント」をご参照ください。

ローカルファイルのアップロード

次の例では、ローカルファイルをメモリに読み込み、3 つのパートに分割し、goroutine を使用してパートを同時にアップロードしてから、アセンブルします。

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
)

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 uploadId string

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

	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	client := oss.NewClient(cfg)

	// マルチパートアップロードを開始し、アップロード ID を取得します。
	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("upload ID: %s\n", *initResult.UploadId)
	uploadId = *initResult.UploadId

	var wg sync.WaitGroup
	var parts []oss.UploadPart
	var mu sync.Mutex
	count := 3

	// ローカルファイルをメモリに読み込みます。「yourLocalFile」を実際のファイルパスに置き換えてください。
	file, err := os.Open("yourLocalFile")
	if err != nil {
		log.Fatalf("failed to open file: %v", err)
	}
	defer file.Close()

	bufReader := bufio.NewReader(file)
	content, err := io.ReadAll(bufReader)
	if err != nil {
		log.Fatalf("failed to read file: %v", err)
	}
	log.Printf("file size: %d bytes\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),
				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)
			}

			mu.Lock()
			parts = append(parts, oss.UploadPart{
				PartNumber: partRequest.PartNumber,
				ETag:       partResult.ETag,
			})
			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("multipart upload complete: %#v\n", result)
}

その他の例

生成されたコンテンツでのテスト

次の例では、400 KB のランダムな文字列を生成し、3 つのパートに分割して同時にアップロードします。これは、実際のファイルなしでアップロードパイプラインをテストするのに役立ちます。

package main

import (
	"bufio"
	"context"
	"flag"
	"io"
	"log"
	"math/rand"
	"strings"
	"sync"
	"time"

	"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
	letters    = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
)

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 uploadId string

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

	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	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("upload ID: %#v\n", initResult)
	uploadId = *initResult.UploadId

	var wg sync.WaitGroup
	var parts []oss.UploadPart
	var mu sync.Mutex
	count := 3

	// 400 KB のランダムな文字列を生成します。
	body := randBody(400000)
	reader := strings.NewReader(body)
	bufReader := bufio.NewReader(reader)
	content, _ := io.ReadAll(bufReader)
	partSize := len(body) / count

	for i := 0; i < count; i++ {
		wg.Add(1)
		go func(partNumber int, partSize int, i int) {
			defer wg.Done()

			partRequest := &oss.UploadPartRequest{
				Bucket:     oss.Ptr(bucketName),
				Key:        oss.Ptr(objectName),
				PartNumber: int32(partNumber),
				UploadId:   oss.Ptr(uploadId),
				Body:       strings.NewReader(string(content[i*partSize : (i+1)*partSize])),
			}

			partResult, err := client.UploadPart(context.TODO(), partRequest)
			if err != nil {
				log.Fatalf("failed to upload part %d: %v", partNumber, err)
			}

			mu.Lock()
			parts = append(parts, oss.UploadPart{
				PartNumber: partRequest.PartNumber,
				ETag:       partResult.ETag,
			})
			mu.Unlock()
		}(i+1, partSize, i)
	}

	wg.Wait()
	log.Println("all parts uploaded")

	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("multipart upload complete: %#v\n", result)
}

// randBody は長さ n のランダムな文字列を生成します。
func randBody(n int) string {
	b := make([]rune, n)
	randMarker := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i := range b {
		b[i] = letters[randMarker.Intn(len(letters))]
	}
	return string(b)
}

マルチパートアップロードの中止

Client.AbortMultipartUpload を使用して、進行中のアップロードをキャンセルし、アップロードされたパートが使用しているストレージを解放します。中止する一般的な理由は次のとおりです。

  • 破損したコンテンツ — アップロード中にオブジェクトのエラー (データの破損や予期しないコンテンツなど) を検出した場合、タスクを中止して使用不可能なパートの保存を回避します。

  • ネットワーク障害 — 接続が中断され、パートが失われたり破損したりする可能性がある場合は、中止して再起動し、データ整合性を確保します。

  • ストレージ不足 — ストレージ容量が懸念される場合は、タスクを中止して、より優先度の高いアップロードのためにリソースを解放します。

  • 誤った開始 — 誤ってアップロードを開始した場合や、間違ったバージョンをアップロードした場合は、中止してキャンセルします。

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
)

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 uploadId string

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

	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	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("upload ID: %s\n", *initResult.UploadId)
	uploadId = *initResult.UploadId

	request := &oss.AbortMultipartUploadRequest{
		Bucket:   oss.Ptr(bucketName),
		Key:      oss.Ptr(objectName),
		UploadId: oss.Ptr(uploadId),
	}
	result, err := client.AbortMultipartUpload(context.TODO(), request)
	if err != nil {
		log.Fatalf("failed to abort multipart upload: %v", err)
	}
	log.Printf("abort result: %#v\n", result)
}

アップロード済みパートのリスト表示

Client.NewListPartsPaginator を使用して、特定のマルチパートアップロードタスクでアップロードされたパートをリスト表示します。ページネーターは、各パートの PartNumberETagLastModifiedSize、および HashCRC64 を返します。一般的なユースケースは次のとおりです。

  • 再開可能なアップロード — 接続が中断された場合、すでにアップロードされているパートをリスト表示して、再アップロードが必要なパートを特定します。

  • 大きなオブジェクトのアップロードのモニタリング — 非常に大きなオブジェクトをアップロードする際に、アップロード済みのパートをリスト表示して、タスクが期待どおりに実行されていることを確認し、問題を早期に検出します。

  • トラブルシューティング — アップロード中にエラーが発生した場合、アップロード済みのパートを検査して、どの特定のパートが失敗したかを特定し、それに応じて問題を解決します。

  • リソース管理 — 厳格なリソース制約があるシナリオでは、アップロードの進捗をモニタリングして、ストレージ容量と帯域幅をより適切に管理します。

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
)

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 uploadId string

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

	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	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("upload ID: %s\n", *initResult.UploadId)
	uploadId = *initResult.UploadId

	request := &oss.ListPartsRequest{
		Bucket:   oss.Ptr(bucketName),
		Key:      oss.Ptr(objectName),
		UploadId: oss.Ptr(uploadId),
	}

	p := client.NewListPartsPaginator(request)

	var i int
	log.Println("uploaded parts:")

	for p.HasNext() {
		i++

		page, err := p.NextPage(context.TODO())
		if err != nil {
			log.Fatalf("failed to get page %d: %v", i, err)
		}

		for _, part := range page.Parts {
			log.Printf("part number: %v, ETag: %v, last modified: %v, size: %v, CRC64: %v\n",
				part.PartNumber,
				oss.ToString(part.ETag),
				oss.ToTime(part.LastModified),
				part.Size,
				oss.ToString(part.HashCRC64))
		}
	}
}

未完了アップロードの検索とクリーンアップ

Client.NewListMultipartUploadsPaginator を使用して、バケット内の進行中のすべてのマルチパートアップロードタスクをリスト表示します。進行中のタスクとは、開始されたがまだ完了または中止されていないタスクのことです。

これは、次のような場合に役立ちます。

  • バッチアップロードのモニタリング — 大量のオブジェクトをアップロードする際に、すべてのアップロードタスクが期待どおりに進行していることを確認します。

  • 停止したアップロードの検出 — ネットワークの問題で停止したタスクを特定し、再開または中止します。

  • リソース使用量の最適化 — 進行中のタスクのステータスに基づいて、帯域幅の割り当てやアップロードポリシーを調整します。

  • データ移行の追跡 — 大規模な移行中にすべてのアクティブなアップロードをモニタリングして、障害を早期に発見し解決します。

パラメーター

パラメーター

説明

Delimiter

オブジェクトを名前でグループ化します。デリミタまでのプレフィックスが同じオブジェクトは、CommonPrefixes の単一のエントリとして返されます。

MaxUploads

返すアップロードタスクの最大数。デフォルト:1000。最大:1000。

KeyMarker

この値よりもアルファベット順で後になるオブジェクト名を持つタスクのみを返します。UploadIDMarker と一緒に使用して、ページネーションの開始点を設定します。

Prefix

このプレフィックスで始まるオブジェクト名を持つタスクのみを返します。

UploadIDMarker

KeyMarker と一緒に使用します。KeyMarker が設定されていない場合、このパラメーターは無視されます。KeyMarker が設定されている場合、結果には KeyMarker より後のオブジェクト名を持つタスクと、KeyMarker と同じオブジェクト名で UploadIDMarker より大きいアップロード ID を持つタスクが含まれます。

次の例では、オブジェクト名が file で始まる最大 100 個のアップロードタスクをリスト表示します。

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
)

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

	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	client := oss.NewClient(cfg)

	request := &oss.ListMultipartUploadsRequest{
		Bucket:     oss.Ptr(bucketName),
		MaxUploads: 100,
		Prefix:     oss.Ptr("file"),
	}

	p := client.NewListMultipartUploadsPaginator(request)

	var i int
	log.Println("ongoing multipart uploads:")

	for p.HasNext() {
		i++

		page, err := p.NextPage(context.TODO())
		if err != nil {
			log.Fatalf("failed to get page %d: %v", i, err)
		}

		for _, u := range page.Uploads {
			log.Printf("key: %v, upload ID: %v, initiated: %v\n",
				oss.ToString(u.Key),
				oss.ToString(u.UploadId),
				oss.ToTime(u.Initiated))
		}
	}
}

コールバックを使用したアップロード

次の例では、400 KB のオブジェクトを 3 つのパートで同時にアップロードし、CompleteMultipartUpload が成功した後に OSS が呼び出すコールバックを設定します。コールバック URL、リクエストボディ、およびカスタム変数は、CompleteMultipartUploadRequest で渡される Base64 エンコードされた JSON 文字列です。

package main

import (
	"bufio"
	"context"
	"encoding/base64"
	"encoding/json"
	"flag"
	"io"
	"log"
	"math/rand"
	"strings"
	"sync"
	"time"

	"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
	letters    = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
)

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

	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	client := oss.NewClient(cfg)

	initRequest := &oss.InitiateMultipartUploadRequest{
		Bucket: oss.Ptr(bucketName),
		Key:    oss.Ptr(objectName),
	}
	initResult, err := client.InitiateMultipartUpload(context.TODO(), initRequest)

	// コールバック設定を構築し、エンコードします。
	callbackMap := map[string]string{
		"callbackUrl":      "https://example.com:23450",
		"callbackBody":     "bucket=${bucket}&object=${object}&size=${size}&my_var_1=${x:my_var1}&my_var_2=${x:my_var2}",
		"callbackBodyType": "application/x-www-form-urlencoded",
	}

	callbackStr, err := json.Marshal(callbackMap)
	if err != nil {
		log.Fatalf("failed to marshal callback config: %v", err)
	}
	callbackBase64 := base64.StdEncoding.EncodeToString(callbackStr)

	callbackVarMap := map[string]string{
		"x:my_var1": "this is var 1",
		"x:my_var2": "this is var 2",
	}
	callbackVarStr, err := json.Marshal(callbackVarMap)
	if err != nil {
		log.Fatalf("failed to marshal callback vars: %v", err)
	}
	callbackVarBase64 := base64.StdEncoding.EncodeToString(callbackVarStr)

	var wg sync.WaitGroup
	var parts []oss.UploadPart
	var mu sync.Mutex
	count := 3
	body := randBody(400000)
	reader := strings.NewReader(body)
	bufReader := bufio.NewReader(reader)
	content, _ := io.ReadAll(bufReader)
	partSize := len(body) / count

	for i := 0; i < count; i++ {
		wg.Add(1)
		go func(partNumber int, partSize int, i int) {
			defer wg.Done()
			partRequest := &oss.UploadPartRequest{
				Bucket:     oss.Ptr(bucketName),
				Key:        oss.Ptr(objectName),
				PartNumber: int32(partNumber),
				UploadId:   initResult.UploadId,
				Body:       strings.NewReader(string(content[i*partSize : (i+1)*partSize])),
			}
			partResult, err := client.UploadPart(context.TODO(), partRequest)
			if err != nil {
				log.Fatalf("failed to upload part %d: %v", partNumber, err)
			}
			mu.Lock()
			parts = append(parts, oss.UploadPart{
				PartNumber: partRequest.PartNumber,
				ETag:       partResult.ETag,
			})
			mu.Unlock()
		}(i+1, partSize, i)
	}
	wg.Wait()

	request := &oss.CompleteMultipartUploadRequest{
		Bucket:   oss.Ptr(bucketName),
		Key:      oss.Ptr(objectName),
		UploadId: initResult.UploadId,
		CompleteMultipartUpload: &oss.CompleteMultipartUpload{
			Parts: parts,
		},
		Callback:    oss.Ptr(callbackBase64),
		CallbackVar: oss.Ptr(callbackVarBase64),
	}
	result, err := client.CompleteMultipartUpload(context.TODO(), request)
	if err != nil {
		log.Fatalf("failed to complete multipart upload: %v", err)
	}
	log.Printf("multipart upload complete: %#v\n", result)
}

func randBody(n int) string {
	b := make([]rune, n)
	randMarker := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i := range b {
		b[i] = letters[randMarker.Intn(len(letters))]
	}
	return string(b)
}

アップロード進捗のモニタリング

ProgressFnUploadPartRequest フィールドを使用して、各パートで転送されたデータ量を追跡します。コールバックは、この増分でアップロードされたバイト数、これまでに転送された合計バイト数、およびこのパートの合計バイト数の 3 つの値を受け取ります。

次の例では、ローカルファイルを 5 つのパートで同時にアップロードし、それぞれの進捗状況を出力します。

package main

import (
	"bufio"
	"bytes"
	"context"
	"flag"
	"fmt"
	"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
)

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 uploadId string

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

	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	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("upload ID: %s\n", *initResult.UploadId)
	uploadId = *initResult.UploadId

	var wg sync.WaitGroup
	var parts []oss.UploadPart
	var mu sync.Mutex
	count := 5

	// 「/Users/yourLocalPath/yourFileName」を実際のファイルパスに置き換えてください。
	file, err := os.Open("/Users/yourLocalPath/yourFileName")
	if err != nil {
		log.Fatalf("failed to open file: %v", err)
	}
	defer file.Close()

	bufReader := bufio.NewReader(file)
	content, err := io.ReadAll(bufReader)
	if err != nil {
		log.Fatalf("failed to read file: %v", err)
	}
	log.Printf("file size: %d bytes\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),
				Body:       bytes.NewReader(content[start:end]),
				ProgressFn: func(increment, transferred, total int64) {
					fmt.Printf("part %d: increment=%d, transferred=%d, total=%d\n",
						partNumber, increment, transferred, total)
				},
			}

			partResult, err := client.UploadPart(context.TODO(), partRequest)
			if err != nil {
				log.Fatalf("failed to upload part %d: %v", partNumber, err)
			}
			log.Printf("uploaded part %d (bytes %d–%d)", partNumber, start, end)

			mu.Lock()
			parts = append(parts, oss.UploadPart{
				PartNumber: partRequest.PartNumber,
				ETag:       partResult.ETag,
			})
			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("multipart upload complete: %#v\n", result)
}

API リファレンス

操作

説明

InitiateMultipartUpload

マルチパートアップロードタスクを開始し、アップロード ID を返します

UploadPart

単一のパートをアップロードします

CompleteMultipartUpload

アップロードされたパートを完全なオブジェクトにアセンブルします

AbortMultipartUpload

マルチパートアップロードタスクをキャンセルします

NewListPartsPaginator

特定のタスクでアップロードされたパートをリスト表示します

NewListMultipartUploadsPaginator

バケット内の進行中のすべてのマルチパートアップロードタスクをリスト表示します。進行中のタスクとは、開始されたがまだ完了または中止されていないタスクのことです。

完全なサンプルコードについては、「GitHub」をご参照ください。