All Products
Search
Document Center

Object Storage Service:Unggah multi-bagian (Go SDK V2)

Last Updated:Mar 21, 2026

Unggah multi-bagian membagi objek besar menjadi beberapa bagian dan mengunggahnya secara independen. Setelah semua bagian diunggah, panggil CompleteMultipartUpload untuk menyusunnya menjadi satu objek utuh.

Prasyarat

Sebelum memulai, pastikan Anda telah memiliki:

Cara kerja

Unggah multi-bagian terdiri dari tiga langkah:

  1. Initiate — Panggil Client.InitiateMultipartUpload untuk mendapatkan ID unggah unik dari OSS.

  2. Upload parts — Panggil Client.UploadPart untuk mengunggah setiap bagian menggunakan ID unggah tersebut.

  3. Complete — Panggil Client.CompleteMultipartUpload untuk menyusun semua bagian yang telah diunggah menjadi objek akhir.

Perilaku penomoran bagian:

  • Nomor bagian menentukan urutan bagian dalam objek akhir. Mengunggah bagian baru dengan nomor bagian yang sudah ada akan menimpa bagian aslinya.

  • OSS mengembalikan hash MD5 dari setiap bagian yang diunggah dalam header respons ETag, lalu memvalidasinya terhadap hash yang dihitung oleh OSS SDK for Go. Jika berbeda, OSS mengembalikan InvalidDigest. Untuk informasi lebih lanjut, lihat Dapatkah saya menggunakan nilai ETag sebagai hash MD5 OSS untuk memeriksa konsistensi data.

Kode contoh menggunakan wilayah cn-hangzhou dan titik akhir publik. Untuk mengakses OSS dari layanan Alibaba Cloud lain di wilayah yang sama, alihkan ke titik akhir internal. Untuk daftar wilayah dan titik akhir yang tersedia, lihat Wilayah dan titik akhir.

Unggah file lokal

Contoh berikut membaca file lokal ke memori, membaginya menjadi 3 bagian, mengunggah bagian-bagian tersebut secara konkuren menggunakan goroutine, lalu menyusunnya.

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)

	// Initiate the multipart upload and get an upload 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

	// Read the local file into memory. Replace "yourLocalFile" with the actual file path.
	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))

	// Split the file into parts and upload them concurrently.
	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()

	// Assemble all uploaded parts into the final object.
	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)
}

Contoh lainnya

Pengujian dengan konten yang dihasilkan

Contoh berikut menghasilkan string acak sebesar 400 KB, membaginya menjadi 3 bagian, lalu mengunggahnya secara konkuren — berguna untuk menguji pipeline unggah Anda tanpa menggunakan file nyata.

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

	// Generate a random 400 KB string.
	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 generates a random string of length 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)
}

Membatalkan unggah multi-bagian

Gunakan Client.AbortMultipartUpload untuk membatalkan unggahan yang sedang berlangsung dan melepaskan penyimpanan yang digunakan oleh bagian-bagian yang telah diunggah. Alasan umum untuk membatalkan:

  • Konten rusak — Jika Anda mendeteksi error pada objek selama unggah (misalnya, data rusak atau konten tidak sesuai), batalkan tugas untuk menghindari penyimpanan bagian yang tidak dapat digunakan.

  • Kegagalan jaringan — Jika koneksi terputus dan bagian mungkin hilang atau rusak, batalkan dan mulai ulang untuk memastikan integritas data.

  • Kapasitas penyimpanan tidak mencukupi — Jika kapasitas penyimpanan menjadi perhatian, batalkan tugas untuk membebaskan sumber daya bagi unggahan prioritas tinggi.

  • Inisiasi tidak disengaja — Jika Anda memulai unggahan secara tidak sengaja atau mengunggah versi yang salah, batalkan untuk membatalkannya.

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

Daftar bagian yang diunggah

Gunakan Client.NewListPartsPaginator untuk mendaftar bagian-bagian yang telah diunggah dalam tugas unggah multi-bagian tertentu. Paginator ini mengembalikan PartNumber, ETag, LastModified, Size, dan HashCRC64 dari setiap bagian. Kasus penggunaan umum meliputi:

  • Unggah yang dapat dilanjutkan — Saat koneksi terputus, daftar bagian yang telah diunggah untuk mengidentifikasi bagian mana yang perlu diunggah ulang.

  • Memantau unggahan objek besar — Saat mengunggah objek yang sangat besar, daftar bagian yang diunggah untuk memverifikasi bahwa tugas berjalan sesuai harapan dan mendeteksi masalah lebih awal.

  • Pemecahan masalah — Jika terjadi error selama unggah, periksa bagian yang diunggah untuk mengidentifikasi bagian spesifik yang gagal, lalu selesaikan masalah tersebut.

  • Manajemen sumber daya — Dalam skenario dengan batasan sumber daya ketat, pantau progres unggah untuk mengelola kapasitas penyimpanan dan bandwidth secara lebih baik.

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

Temukan dan bersihkan unggahan yang belum selesai

Gunakan Client.NewListMultipartUploadsPaginator untuk mendaftar semua tugas unggah multi-bagian yang sedang berlangsung di dalam bucket. Tugas yang sedang berlangsung adalah tugas yang telah dimulai tetapi belum diselesaikan atau dibatalkan.

Ini berguna saat Anda perlu:

  • Memantau unggahan batch — Memverifikasi semua tugas unggah berjalan sesuai harapan saat mengunggah banyak objek sekaligus.

  • Mendeteksi unggahan yang macet — Mengidentifikasi tugas yang terhenti karena masalah jaringan dan melanjutkan atau membatalkannya.

  • Mengoptimalkan penggunaan sumber daya — Menyesuaikan alokasi bandwidth atau kebijakan unggah berdasarkan status tugas yang sedang berlangsung.

  • Memantau migrasi data — Memantau semua unggahan aktif selama migrasi skala besar untuk mendeteksi dan menyelesaikan kegagalan lebih awal.

Parameter

Parameter

Description

Delimiter

Mengelompokkan objek berdasarkan nama. Objek yang namanya memiliki awalan yang sama hingga delimiter dikembalikan sebagai satu entri dalam CommonPrefixes.

MaxUploads

Jumlah maksimum tugas unggah yang dikembalikan. Default: 1000. Maksimum: 1000.

KeyMarker

Hanya mengembalikan tugas dengan nama objek yang secara alfabetis berada setelah nilai ini. Gunakan bersama UploadIDMarker untuk menetapkan titik awal pagination.

Prefix

Hanya mengembalikan tugas yang nama objeknya diawali dengan awalan ini.

UploadIDMarker

Digunakan bersama KeyMarker. Jika KeyMarker tidak ditetapkan, parameter ini diabaikan. Jika KeyMarker ditetapkan, hasil mencakup: tugas dengan nama objek setelah KeyMarker, dan tugas dengan nama objek yang sama dengan KeyMarker tetapi dengan ID unggah lebih besar dari UploadIDMarker.

Contoh berikut mendaftar hingga 100 tugas unggah yang nama objeknya diawali dengan file:

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

Unggah dengan callback

Contoh berikut mengunggah objek 400 KB dalam 3 bagian konkuren dan mengonfigurasi callback yang dipanggil OSS setelah CompleteMultipartUpload berhasil. URL callback, badan permintaan, dan variabel kustom adalah string JSON yang diencode Base64 yang diteruskan dalam CompleteMultipartUploadRequest.

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)

	// Build and encode the callback configuration.
	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)
}

Pantau progres unggah

Gunakan bidang ProgressFn dalam UploadPartRequest untuk melacak berapa banyak data yang telah ditransfer untuk setiap bagian. Callback menerima tiga nilai: byte yang diunggah dalam increment ini, total byte yang telah ditransfer sejauh ini, dan total byte untuk bagian ini.

Contoh berikut mengunggah file lokal dalam 5 bagian konkuren dan mencetak progres untuk masing-masing.

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

	// Replace "/Users/yourLocalPath/yourFileName" with the actual file path.
	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)
}

Referensi API

Operasi

Description

InitiateMultipartUpload

Memulai tugas unggah multi-bagian dan mengembalikan ID unggah

UploadPart

Mengunggah satu bagian

CompleteMultipartUpload

Menyusun bagian yang diunggah menjadi objek utuh

AbortMultipartUpload

Membatalkan tugas unggah multi-bagian

NewListPartsPaginator

Mendaftar bagian yang diunggah dalam tugas tertentu

NewListMultipartUploadsPaginator

Mendaftar semua tugas unggah multi-bagian yang sedang berlangsung di dalam bucket. Tugas yang sedang berlangsung adalah tugas yang telah dimulai tetapi belum diselesaikan atau dibatalkan.

Untuk kode contoh lengkap, lihat GitHub.