Multipart upload (Go SDK V2)

Updated at:

The multipart upload feature in Object Storage Service (OSS) lets you split a large object into multiple parts. You can upload these parts separately. After all parts are uploaded, you can call the CompleteMultipartUpload operation to combine the parts into a complete object.

Notes

  • The sample code in this topic uses the cn-hangzhou region ID for the China (Hangzhou) region and uses a public endpoint by default. If you access OSS from other Alibaba Cloud services in the same region, use an internal endpoint. For more information about OSS regions and endpoints, see Regions and endpoints.

  • The examples in this topic read access credentials from environment variables. For more information about how to configure access credentials, see Configure access credentials.

  • To perform a multipart upload, you must have the oss:PutObject permission. For more information, see Grant custom permissions to a RAM user.

Multipart upload process

A multipart upload consists of the following three steps:

  1. Initialize a multipart upload event.

    Call the Client.InitiateMultipartUpload method to obtain a globally unique upload ID from OSS.

  2. Upload parts.

    Call the Client.UploadPart method to upload the parts.

    Note
    • For a given upload ID, the part number identifies the position of the part within the object. If you upload a new part with an existing part number, OSS overwrites the existing part.

    • OSS includes the MD5 hash of the received part in the ETag header of the response.

    • OSS calculates the MD5 hash of the uploaded data and compares it with the hash calculated by the software development kit (SDK). If the hashes do not match, OSS returns an `InvalidDigest` error code.

  3. Complete the multipart upload.

    After all parts are uploaded, call the Client.CompleteMultipartUpload method to combine all parts into a complete object.

Sample code

The following code shows how to split a large local file into multiple parts, upload the parts to a bucket concurrently, and then combine the parts into a complete object.

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

// Define global variables.
var (
	region     string // The region where the bucket is located.
	bucketName string // The name of the source bucket.
	objectName string // The name of the source object.

)

// The init function initializes command-line parameters.
func init() {
	flag.StringVar(&region, "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() {
	// Parse command-line parameters.
	flag.Parse()

	// Define the upload ID.
	var uploadId string

	// Check if the source bucket name is empty.
	if len(bucketName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, source bucket name required")
	}

	// Check if the region is empty.
	if len(region) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, region required")
	}

	// Check if the source object name is empty.
	if len(objectName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, source object name required")
	}

	// Load default configurations and set the credential provider and region.
	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	// Create an OSS client.
	client := oss.NewClient(cfg)

	// Initialize the multipart upload request.
	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)
	}

	// Print the result of the multipart upload initialization.
	log.Printf("initiate multipart upload result:%#v\n", *initResult.UploadId)
	uploadId = *initResult.UploadId

	// Initialize the wait group and mutex.
	var wg sync.WaitGroup
	var parts []oss.UploadPart
	count := 3
	var mu sync.Mutex

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

	// Calculate the size of each part.
	chunkSize := len(content) / count
	if chunkSize == 0 {
		chunkSize = 1
	}

	// Start multiple goroutines to upload parts concurrently.
	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()

			// Create an upload part request.
			partRequest := &oss.UploadPartRequest{
				Bucket:     oss.Ptr(bucketName),                 // The name of the destination bucket.
				Key:        oss.Ptr(objectName),                 // The name of the destination object.
				PartNumber: int32(partNumber),                   // The part number.
				UploadId:   oss.Ptr(uploadId),                   // The upload ID.
				Body:       bytes.NewReader(content[start:end]), // The content of the part.
			}

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

			// Record the result of the part upload.
			part := oss.UploadPart{
				PartNumber: partRequest.PartNumber,
				ETag:       partResult.ETag,
			}

			// Use a mutex to protect shared data.
			mu.Lock()
			parts = append(parts, part)
			mu.Unlock()
		}(i+1, start, end)
	}

	// Wait for all goroutines to complete.
	wg.Wait()

	// Complete the multipart upload request.
	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)
	}

	// Print the result of the completed multipart upload.
	log.Printf("complete multipart upload result:%#v\n", result)
}

Common scenarios

Upload a random string of a specified length using multipart upload

The following code shows how to split a 400 KB random string into three parts, upload the parts to a bucket concurrently, and then combine the parts into a complete object.

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

// Define global variables.
var (
	region     string                                                                     // The region where the bucket is located.
	bucketName string                                                                     // The name of the bucket.
	objectName string                                                                     // The name of the object.
	letters    = []rune("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") // The character set for generating random strings.
)

// The init function initializes command-line parameters.
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() {
	// Parse command-line parameters.
	flag.Parse()

	// Define the upload ID.
	var uploadId string

	// Check if the bucket name is empty.
	if len(bucketName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, bucket name required")
	}

	// Check if the region is empty.
	if len(region) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, region required")
	}

	// Check if the object name is empty.
	if len(objectName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, object name required")
	}

	// Load default configurations and set the credential provider and region.
	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	// Create an OSS client.
	client := oss.NewClient(cfg)

	// Create a request to initialize the multipart upload.
	initRequest := &oss.InitiateMultipartUploadRequest{
		Bucket: oss.Ptr(bucketName), // The name of the bucket.
		Key:    oss.Ptr(objectName), // The name of the object.
	}

	// Initialize the multipart upload and handle the result.
	initResult, err := client.InitiateMultipartUpload(context.TODO(), initRequest)
	if err != nil {
		log.Fatalf("failed to initiate multipart upload %v", err)
	}

	// Print the result of the multipart upload initialization.
	log.Printf("initiate multipart upload result:%#v\n", initResult)
	uploadId = *initResult.UploadId

	// Initialize the wait group and mutex.
	var wg sync.WaitGroup
	var parts []oss.UploadPart
	count := 3
	body := randBody(400000) // Generate a 400 KB random string.
	reader := strings.NewReader(body)
	bufReader := bufio.NewReader(reader)
	content, _ := io.ReadAll(bufReader)
	partSize := len(body) / count
	var mu sync.Mutex

	// Start multiple goroutines to upload parts concurrently.
	for i := 0; i < count; i++ {
		wg.Add(1)
		go func(partNumber int, partSize int, i int) {
			defer wg.Done()

			// Create an upload part request.
			partRequest := &oss.UploadPartRequest{
				Bucket:     oss.Ptr(bucketName),                                             // The name of the bucket.
				Key:        oss.Ptr(objectName),                                             // The name of the object.
				PartNumber: int32(partNumber),                                               // The part number.
				UploadId:   oss.Ptr(uploadId),                                               // The upload ID.
				Body:       strings.NewReader(string(content[i*partSize : (i+1)*partSize])), // The content of the part.
			}

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

			// Record the result of the part upload.
			part := oss.UploadPart{
				PartNumber: partRequest.PartNumber,
				ETag:       partResult.ETag,
			}

			// Use a mutex to protect shared data.
			mu.Lock()
			parts = append(parts, part)
			mu.Unlock()
		}(i+1, partSize, i)
	}

	// Wait for all goroutines to complete.
	wg.Wait()

	// Print a message indicating that the parts were uploaded successfully.
	log.Println("upload part success!")

	// Create a request to complete the multipart upload.
	request := &oss.CompleteMultipartUploadRequest{
		Bucket:   oss.Ptr(bucketName),
		Key:      oss.Ptr(objectName),
		UploadId: oss.Ptr(uploadId),
		CompleteMultipartUpload: &oss.CompleteMultipartUpload{
			Parts: parts,
		},
	}

	// Complete the multipart upload and handle the result.
	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)
}

// randBody generates a random string of a specified length.
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)
}

Cancel a specific multipart upload event

In the following scenarios, you can use the Client.AbortMultipartUpload method to cancel a multipart upload event.

  1. File errors:

    • If you detect an error in the file during the upload, such as corruption or malicious code, you can cancel the upload to prevent potential threats.

  2. Unstable network:

    • If the network connection is unstable or interrupted, parts may be lost or damaged. You can cancel the upload and restart it to ensure data integrity and consistency.

  3. Resource limits:

    • If you have limited storage space and the file is too large, you can cancel the upload to free up storage resources for more important tasks.

  4. Accidental operation:

    • If you accidentally start an unnecessary upload or upload the wrong file version, you can cancel the upload event.

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

// Define global variables.
var (
	region     string // The region where the bucket is located.
	bucketName string // The name of the source bucket.
	objectName string // The name of the source object.

)

// The init function initializes command-line parameters.
func init() {
	flag.StringVar(&region, "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() {
	// Parse command-line parameters.
	flag.Parse()

	// Define the upload ID.
	var uploadId string

	// Check if the source bucket name is empty.
	if len(bucketName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, source bucket name required")
	}

	// Check if the region is empty.
	if len(region) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, region required")
	}

	// Check if the source object name is empty.
	if len(objectName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, source object name required")
	}

	// Load default configurations and set the credential provider and region.
	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	// Create an OSS client.
	client := oss.NewClient(cfg)

	// Initialize the multipart upload request.
	initRequest := &oss.InitiateMultipartUploadRequest{
		Bucket: oss.Ptr(bucketName),
		Key:    oss.Ptr(objectName),
	}

	// Execute the multipart upload initialization request.
	initResult, err := client.InitiateMultipartUpload(context.TODO(), initRequest)
	if err != nil {
		log.Fatalf("failed to initiate multipart upload %v", err)
	}

	// Print the result of the multipart upload initialization.
	log.Printf("initiate multipart upload result:%#v\n", *initResult.UploadId)
	uploadId = *initResult.UploadId

	// Create an AbortMultipartUploadRequest request.
	request := &oss.AbortMultipartUploadRequest{
		Bucket:   oss.Ptr(bucketName), // The name of the bucket.
		Key:      oss.Ptr(objectName), // The name of the object.
		UploadId: oss.Ptr(uploadId),   // The upload ID.
	}
	// Execute the request and handle the result.
	result, err := client.AbortMultipartUpload(context.TODO(), request)
	if err != nil {
		log.Fatalf("failed to abort multipart upload %v", err)
	}
	log.Printf("abort multipart upload result:%#v\n", result)

}

List successfully uploaded parts in a specific multipart upload event

In the following scenarios, you can use the Client.NewListPartsPaginator paginator to list the successfully uploaded parts in a multipart upload event.

Monitor upload progress:

  1. Large file uploads:

    • When uploading a very large file, you can list the uploaded parts to ensure the process is running as expected and to identify problems quickly.

  2. Resumable uploads:

    • If the network is unstable or the upload is interrupted, you can check the uploaded parts to decide whether to retry uploading the remaining parts. This enables resumable uploads.

  3. Troubleshooting:

    • If an error occurs during the upload, you can check the uploaded parts to quickly locate the problem, such as a specific part that failed to upload. Then, you can resolve the issue.

  4. Resource management:

    • For scenarios that require strict resource control, you can monitor the upload progress to better manage storage space and bandwidth and to ensure effective resource use.

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

// Define global variables.
var (
	region     string // The region where the bucket is located.
	bucketName string // The name of the source bucket.
	objectName string // The name of the source object.

)

// The init function initializes command-line parameters.
func init() {
	flag.StringVar(&region, "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() {
	// Parse command-line parameters.
	flag.Parse()

	// Define the upload ID.
	var uploadId string

	// Check if the source bucket name is empty.
	if len(bucketName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, source bucket name required")
	}

	// Check if the region is empty.
	if len(region) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, region required")
	}

	// Check if the source object name is empty.
	if len(objectName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, source object name required")
	}

	// Load default configurations and set the credential provider and region.
	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	// Create an OSS client.
	client := oss.NewClient(cfg)

	// Initialize the multipart upload request.
	initRequest := &oss.InitiateMultipartUploadRequest{
		Bucket: oss.Ptr(bucketName),
		Key:    oss.Ptr(objectName),
	}

	// Execute the multipart upload initialization request.
	initResult, err := client.InitiateMultipartUpload(context.TODO(), initRequest)
	if err != nil {
		log.Fatalf("failed to initiate multipart upload %v", err)
	}

	// Print the result of the multipart upload initialization.
	log.Printf("initiate multipart upload result:%#v\n", *initResult.UploadId)
	uploadId = *initResult.UploadId

	// Create a request to list parts.
	request := &oss.ListPartsRequest{
		Bucket:   oss.Ptr(bucketName), // The name of the bucket.
		Key:      oss.Ptr(objectName), // The name of the object.
		UploadId: oss.Ptr(uploadId),   // The upload ID.
	}

	// Create a paginator.
	p := client.NewListPartsPaginator(request)

	// Initialize the page counter.
	var i int
	log.Println("List Parts:")

	// Traverse each page in the paginator.
	for p.HasNext() {
		i++

		// Get the data of the next page.
		page, err := p.NextPage(context.TODO())
		if err != nil {
			log.Fatalf("failed to get page %v, %v", i, err)
		}

		// Print the information of each part on the page.
		for _, part := range page.Parts {
			log.Printf("Part Number: %v, ETag: %v, Last Modified: %v, Size: %v, HashCRC64: %v\n",
				part.PartNumber,
				oss.ToString(part.ETag),
				oss.ToTime(part.LastModified),
				part.Size,
				oss.ToString(part.HashCRC64))
		}
	}

}

List multipart upload events

In the following scenarios, you can use the Client.NewListMultipartUploadsPaginator paginator to list all in-progress multipart upload events in a bucket.

Monitoring scenarios:

  1. Batch file upload management:

    • When you need to upload many files, you can use the ListMultipartUploads method to monitor all multipart upload activities in real time. This ensures that all files are uploaded correctly.

  2. Fault detection and recovery:

    • If network issues or other faults occur during an upload, some parts may fail to upload. By monitoring in-progress multipart upload events, you can promptly detect these issues and take action to resume the upload.

  3. Resource optimization and management:

    • During large-scale file uploads, monitoring in-progress multipart upload events helps optimize resource allocation. For example, you can adjust bandwidth usage or optimize the upload policy based on the upload progress.

  4. Data migration:

    • When performing a large-scale data migration project, you can monitor all in-progress multipart upload events to ensure the migration task proceeds smoothly and to promptly identify and resolve any potential issues.

Parameter settings

Parameter

Description

Delimiter

The character used to group object names. All objects that have the same prefix up to the first occurrence of the delimiter are grouped as a single element.

MaxUploads

The maximum number of multipart upload events to return. The default and maximum value is 1000.

KeyMarker

Specifies the starting point for the list. The list includes multipart upload events for objects with names that are lexicographically after the `KeyMarker` value. Use this parameter with `UploadIDMarker`.

Prefix

Filters the response to include only objects whose names begin with the specified prefix. Note that the returned object names still contain the prefix.

UploadIDMarker

Use with `KeyMarker` to specify the starting point of the list.

  • If `KeyMarker` is not set, OSS ignores this parameter.

  • If `KeyMarker` is set, the query returns:

    • Multipart upload events for objects whose names are lexicographically after the `KeyMarker` value.

    • Multipart upload events for objects whose names match the `KeyMarker` value, but whose upload IDs are lexicographically after the `UploadIDMarker` value.

  • Specify the prefix `file` and return a maximum of 100 results

    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"
    )
    
    // Define global variables.
    var (
    	region     string // The region where the bucket is located.
    	bucketName string // The name of the source bucket.
    	objectName string // The name of the source object.
    
    )
    
    // The init function initializes command-line parameters.
    func init() {
    	flag.StringVar(&region, "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() {
    	// Parse command-line parameters.
    	flag.Parse()
    
    	// Check if the source bucket name is empty.
    	if len(bucketName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, source bucket name required")
    	}
    
    	// Check if the region is empty.
    	if len(region) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, region required")
    	}
    
    	// Check if the source object name is empty.
    	if len(objectName) == 0 {
    		flag.PrintDefaults()
    		log.Fatalf("invalid parameters, source object name required")
    	}
    
    	// Load default configurations and set the credential provider and region.
    	cfg := oss.LoadDefaultConfig().
    		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
    		WithRegion(region)
    
    	// Create an OSS client.
    	client := oss.NewClient(cfg)
    
    	// Create a request to list multipart uploads.
    	request := &oss.ListMultipartUploadsRequest{
    		Bucket:     oss.Ptr(bucketName), // The name of the bucket.
    		MaxUploads: 100,                 // Specify that a maximum of 100 results are returned.
    		Prefix:     oss.Ptr("file"),     // Specify the prefix as file.
    	}
    
    	// Create a paginator.
    	p := client.NewListMultipartUploadsPaginator(request)
    
    	var i int
    	log.Println("List Multipart Uploads:")
    
    	// Traverse each page in the paginator.
    	for p.HasNext() {
    		i++
    
    		// Get the data of the next page.
    		page, err := p.NextPage(context.TODO())
    		if err != nil {
    			log.Fatalf("failed to get page %v, %v", i, err)
    		}
    
    		// Print the information of each multipart upload on the page.
    		for _, u := range page.Uploads {
    			log.Printf("Upload key: %v, upload id: %v, initiated: %v\n", oss.ToString(u.Key), oss.ToString(u.UploadId), oss.ToTime(u.Initiated))
    		}
    	}
    
    }
    

Multipart upload with callbacks

The following code shows how to split a 400 KB file into three parts, upload them concurrently to Alibaba Cloud OSS, combine the parts into a complete object after the upload is complete, and trigger a callback notification after the parts are combined.

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)

	// Define callback parameters.
	callbackMap := map[string]string{
		"callbackUrl":      "https://example.com:23450",                                                                  // Set the URL of the callback server, for example, https://example.com:23450.
		"callbackBody":     "bucket=${bucket}&object=${object}&size=${size}&my_var_1=${x:my_var1}&my_var_2=${x:my_var2}", // Set the callback request body.
		"callbackBodyType": "application/x-www-form-urlencoded",                                                          // Set the callback request body type.
	}

	// Convert the callback parameters to JSON and encode them in Base64 to pass them as callback parameters.
	callbackStr, err := json.Marshal(callbackMap)
	if err != nil {
		log.Fatalf("failed to marshal callback map: %v", err)
	}
	callbackBase64 := base64.StdEncoding.EncodeToString(callbackStr)

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

	var wg sync.WaitGroup
	var parts []oss.UploadPart
	count := 3
	body := randBody(400000)
	reader := strings.NewReader(body)
	bufReader := bufio.NewReader(reader)
	content, _ := io.ReadAll(bufReader)
	partSize := len(body) / count
	var mu sync.Mutex
	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)
			}
			part := oss.UploadPart{
				PartNumber: partRequest.PartNumber,
				ETag:       partResult.ETag,
			}
			mu.Lock()
			parts = append(parts, part)
			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), // Specify the callback parameters.
		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("complete multipart upload result:%#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)
}

Multipart upload with a progress bar

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

// Define global variables.
var (
	region     string // The region where the bucket is located.
	bucketName string // The name of the source bucket.
	objectName string // The name of the source object.

)

// The init function initializes command-line parameters.
func init() {
	flag.StringVar(&region, "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() {
	// Parse command-line parameters.
	flag.Parse()

	// Define the upload ID.
	var uploadId string

	// Check if the source bucket name is empty.
	if len(bucketName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, source bucket name required")
	}

	// Check if the region is empty.
	if len(region) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, region required")
	}

	// Check if the source object name is empty.
	if len(objectName) == 0 {
		flag.PrintDefaults()
		log.Fatalf("invalid parameters, source object name required")
	}

	// Load default configurations and set the credential provider and region.
	cfg := oss.LoadDefaultConfig().
		WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
		WithRegion(region)

	// Create an OSS client.
	client := oss.NewClient(cfg)

	// Initialize the multipart upload request.
	initRequest := &oss.InitiateMultipartUploadRequest{
		Bucket:  oss.Ptr(bucketName),
		Key:     oss.Ptr(objectName),
	}

	// Execute the multipart upload initialization request.
	initResult, err := client.InitiateMultipartUpload(context.TODO(), initRequest)
	if err != nil {
		log.Fatalf("failed to initiate multipart upload %v", err)
	}

	// Print the result of the multipart upload initialization.
	log.Printf("initiate multipart upload result:%#v\n", *initResult.UploadId)
	uploadId = *initResult.UploadId

	// Initialize the wait group and mutex.
	var wg sync.WaitGroup
	var parts []oss.UploadPart
	count := 5
	var mu sync.Mutex

	// Read the local file into memory. Replace /Users/yourLocalPath/yourFileName with the actual path and name of your local file.
	file, err := os.Open("/Users/yourLocalPath/yourFileName")
	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))

	// Calculate the size of each part.
	chunkSize := len(content) / count
	if chunkSize == 0 {
		chunkSize = 1
	}

	// Start multiple goroutines to upload parts concurrently.
	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()

			// Create an upload part request.
			partRequest := &oss.UploadPartRequest{
				Bucket:     oss.Ptr(bucketName),                 // The name of the destination bucket.
				Key:        oss.Ptr(objectName),                 // The name of the destination object.
				PartNumber: int32(partNumber),                   // The part number.
				UploadId:   oss.Ptr(uploadId),                   // The upload ID.
				Body:       bytes.NewReader(content[start:end]), // The content of the part.
				ProgressFn: func(increment, transferred, total int64) {
					fmt.Printf("increment:%v, transferred:%v, total:%v\n", increment, transferred, total)
				}, // The progress callback function, used to display the upload progress.
			}

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

			log.Printf("successfully uploaded part %d (start: %d, end: %d)", partNumber, start, end)

			// Record the result of the part upload.
			part := oss.UploadPart{
				PartNumber: partRequest.PartNumber,
				ETag:       partResult.ETag,
			}

			// Use a mutex to protect shared data.
			mu.Lock()
			parts = append(parts, part)
			mu.Unlock()
		}(i+1, start, end)
	}

	// Wait for all goroutines to complete.
	wg.Wait()

	// Complete the multipart upload request.
	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)
	}

	// Print the versionId of the completed multipart upload.
	log.Printf("complete multipart upload result versionId:%#v\n", result)
}

References

  • For the complete sample code for multipart upload, see the GitHub example.

  • A complete multipart upload involves three API operations:

    • For information about the API operation to initialize a multipart upload event, see InitiateMultipartUpload.

    • For information about the API operation to upload a part, see UploadPart.

    • For information about the API operation to complete a multipart upload, see CompleteMultipartUpload.

  • For information about the API operation to cancel a multipart upload event, see AbortMultipartUpload.

  • For information about the API operation to list uploaded parts, see NewListPartsPaginator.

  • For information about the API operation to list all in-progress multipart upload events, which are events that are initiated but not yet completed or canceled, see NewListMultipartUploadsPaginator.