全部產品
Search
文件中心

Object Storage Service:Go分區上傳

更新時間:Oct 26, 2024

OSS提供的分區上傳(Multipart Upload)功能,將要上傳的較大檔案(Object)分成多個分區(Part)來分別上傳,上傳完成後再調用CompleteMultipartUpload介面將這些Part組合成一個Object來達到斷點續傳的效果。

注意事項

  • 本文以華東1(杭州)外網Endpoint為例。如果您希望通過與OSS同地區的其他阿里雲產品訪問OSS,請使用內網Endpoint。關於OSS支援的Region與Endpoint的對應關係,請參見OSS訪問網域名稱、資料中心、開放連接埠

  • 本文以從環境變數讀取存取憑證為例。如何配置訪問憑證,請參見配置訪問憑證

  • 本文以OSS網域名稱建立OSSClient為例。如果您希望通過自訂網域名、STS等方式建立OSSClient,請參見初始化

  • 要分區上傳,您必須有oss:PutObject許可權。具體操作,請參見為RAM使用者授權自訂的權限原則

  • Go SDK 2.2.5及以上版本支援以下範例程式碼中包含的所有屬性。

分區上傳流程

分區上傳(Multipart Upload)分為以下三個步驟:

  1. 初始化一個分區上傳事件。

    調用Bucket.InitiateMultipartUpload方法返回OSS建立的全域唯一的uploadID。

  2. 上傳分區。

    調用Bucket.UploadPart方法上傳分區資料。

    說明
    • 對於同一個uploadID,分區號(partNumber)標識了該分區在整個檔案內的相對位置。如果使用同一個分區號上傳了新的資料,那麼OSS上該分區已有的資料將會被覆蓋。

    • OSS將收到的分區資料的MD5值放在ETag頭內返回給使用者。

    • OSS計算上傳資料的MD5值,並與SDK計算的MD5值比較,如果不一致則返回InvalidDigest錯誤碼。

  3. 完成分區上傳。

    所有分區上傳完成後,調用Bucket.CompleteMultipartUpload方法將所有分區合并成完整的檔案。

範例程式碼

您可以使用以下代碼進行一次完整的分區上傳流程。

package main

import (
	"fmt"
	"log"
	"os"

	"github.com/aliyun/aliyun-oss-go-sdk/oss"
)

func main() {
	// 從環境變數中擷取訪問憑證。運行本程式碼範例之前,請確保已設定環境變數OSS_ACCESS_KEY_ID和OSS_ACCESS_KEY_SECRET。
	provider, err := oss.NewEnvironmentVariableCredentialsProvider()
	if err != nil {
		log.Fatalf("Error: %v", err)
	}

	// 建立OSSClient執行個體。
	// yourEndpoint填寫Bucket對應的Endpoint,以華東1(杭州)為例,填寫為https://oss-cn-hangzhou.aliyuncs.com。其它Region請按實際情況填寫。
	// yourRegion填寫Bucket所在地區,以華東1(杭州)為例,填寫為cn-hangzhou。其它Region請按實際情況填寫。
	clientOptions := []oss.ClientOption{oss.SetCredentialsProvider(&provider)}
	clientOptions = append(clientOptions, oss.Region("yourRegion"))
	// 設定簽名版本
	clientOptions = append(clientOptions, oss.AuthVersion(oss.AuthV4))
	client, err := oss.New("yourEndpoint", "", "", clientOptions...)
	if err != nil {
		log.Fatalf("Error: %v", err)
	}

	// 設定儲存空間名稱
	bucketName := "examplebucket"
	// 設定Object完整路徑。Object完整路徑中不能包含Bucket名稱。
	objectName := "exampleobject.txt"
	// 設定本地檔案的完整路徑。如果未指定本地路徑,則預設從樣本程式所屬專案對應本地路徑中上傳檔案。
	localFilename := "/localpath/exampleobject.txt"

	bucket, err := client.Bucket(bucketName)
	if err != nil {
		log.Fatalf("Error: %v", err)
	}

	// 設定分區大小(單位:位元組),指定5MB為分區大小。
	partSize := int64(5 * 1024 * 1024)

	// 調用分區上傳函數。
	if err := uploadMultipart(bucket, objectName, localFilename, partSize); err != nil {
		log.Fatalf("Failed to upload multipart: %v", err)
	}

}

// 分區上傳函數
func uploadMultipart(bucket *oss.Bucket, objectName, localFilename string, partSize int64) error {
	// 將本地檔案分區
	chunks, err := oss.SplitFileByPartSize(localFilename, partSize)
	if err != nil {
		return fmt.Errorf("failed to split file into chunks: %w", err)
	}

	// 開啟本地檔案。
	file, err := os.Open(localFilename)
	if err != nil {
		return fmt.Errorf("failed to open file: %w", err)
	}
	defer file.Close()

	// 步驟1:初始化一個分區上傳事件。
	imur, err := bucket.InitiateMultipartUpload(objectName)
	if err != nil {
		return fmt.Errorf("failed to initiate multipart upload: %w", err)
	}

	// 步驟2:上傳分區。
	var parts []oss.UploadPart
	for _, chunk := range chunks {
		part, err := bucket.UploadPart(imur, file, chunk.Size, chunk.Number)
		if err != nil {
			// 如果上傳某個部分失敗,嘗試取消整個上傳任務。
			if abortErr := bucket.AbortMultipartUpload(imur); abortErr != nil {
				log.Printf("Failed to abort multipart upload: %v", abortErr)
			}
			return fmt.Errorf("failed to upload part: %w", err)
		}
		parts = append(parts, part)
	}

	// 指定Object的讀寫權限為私人,預設為繼承Bucket的讀寫權限。
	objectAcl := oss.ObjectACL(oss.ACLPrivate)

	// 步驟3:完成分區上傳。
	_, err = bucket.CompleteMultipartUpload(imur, parts, objectAcl)
	if err != nil {
		// 如果完成上傳失敗,嘗試取消上傳。
		if abortErr := bucket.AbortMultipartUpload(imur); abortErr != nil {
			log.Printf("Failed to abort multipart upload: %v", abortErr)
		}
		return fmt.Errorf("failed to complete multipart upload: %w", err)
	}

	log.Printf("Multipart upload completed successfully.")
	return nil
}

常見問題

如何取消分區上傳事件?

當您遇到以下情境時,可以使用Bucket.AbortMultipartUpload方法取消分區上傳事件。

  1. 檔案出錯

    • 如果在上傳過程中發現檔案有錯誤,如檔案損壞或包含惡意代碼,您可以選擇取消上傳以避免潛在的風險。

  2. 網路不穩定

    • 當網路連接不穩定或中斷時,可能會導致上傳過程中的分區丟失或損壞,您可以選擇取消上傳並重新開始,以確保資料的完整性和一致性。

  3. 資源限制

    • 當您的儲存空間有限,而上傳的檔案過大,您可以取消上傳以釋放儲存資源,將資源分派給其他更重要的任務。

  4. 誤操作:

    • 當不小心啟動了一個不必要的上傳任務,或者上傳了一個錯誤的檔案版本,您可以取消此次上傳事件

...
if err = bucket.AbortMultipartUpload(imur); err != nil {
log.Fatalf("failed to abort multipart upload: %w", err)
}

log.Printf("Multipart upload aborted successfully.")

如何列舉已上傳的分區?

當您遇到以下情境時,可以使用Bucket.ListUploadedParts方法列舉某個分區上傳事件中已經成功上傳的分區。

監控上傳進度:

  1. 大型檔案上傳

    • 當上傳非常大的檔案時,您通過列舉已上傳的分區,確保上傳過程按照預期進行,並及時發現問題。

  2. 斷點續傳

    • 在網路不穩定或上傳過程中發生中斷時,您可以通過查看已上傳的分區來決定是否需要重試上傳未完成的部分,從而實現斷點續傳。

  3. 故障排除

    • 如果上傳過程中出現錯誤,通過檢查已上傳的分區,您可以快速定位問題所在,比如某個特定分區上傳失敗,然後針對性地解決問題。

  4. 資源管理

    • 對於需要嚴格控制資源使用方式的情境,通過監控上傳進度,可以更好地管理儲存空間和頻寬資源,確保資源的有效利用。

...	
if lsRes, err := bucket.ListUploadedParts(imur); err != nil {
log.Fatalf("Failed to list uploaded parts: %v", err)
}

for _, upload := range lsRes.UploadedParts {
log.Printf("List PartNumber: %d, ETag: %s, LastModified: %v\n", upload.PartNumber, upload.ETag, upload.LastModified)
}

列舉分區上傳事件

當您遇到以下情境時,可以使用Bucket.ListMultipartUploads方法列舉某個儲存空間所有進行中的分區上傳事件。

監控情境:

  1. 批量檔案上傳管理

    • 當需要上傳大量檔案時,為了確保所有檔案都能正確完成分區上傳,您可以使用ListMultipartUploads方法來即時監控所有的分區上傳活動。

  2. 故障檢測與恢複

    • 在上傳過程中如果遇到網路問題或其他故障,可能導致部分分區未能成功上傳。通過監控進行中中的分區上傳事件,可以及時發現這些問題,並採取措施恢複上傳。

  3. 資源最佳化與管理

    • 在大規模的檔案上傳過程中,監控進行中中的分區上傳事件可以協助最佳化資源分派,例如根據上傳進度調整頻寬使用或最佳化上傳策略。

  4. 資料移轉:

    • 在進行大規模的資料移轉專案時,監控所有進行中的分區上傳事件可以確保遷移任務的順利進行,及時發現並解決任何潛在的問題。

參數設定

參數

說明

Delimiter

用於對Object名字進行分組的字元。所有名字包含指定的首碼且第一次出現Delimiter字元之間的Object作為一組元素。

MaxUploads

限定此次返回分區上傳事件的最大數目,預設值和最大值均為1000。

KeyMarker

所有檔案名稱的字母序大於KeyMarker參數值的分區上傳事件,可以與UploadIDMarker參數一同使用來指定返回結果的起始位置。

Prefix

限定返回的檔案名稱必須以指定的Prefix作為首碼。注意使用Prefix查詢時,返回的檔案名稱中仍會包含Prefix。

UploadIDMarker

與KeyMarker參數一同使用來指定返回結果的起始位置。

  • 如果KeyMarker參數未設定,則OSS忽略該參數。

  • 如果KeyMarker參數被設定,查詢結果中包含:

    • 所有Object名字的字典序大於KeyMarker參數值的分區上傳事件。

    • Object名字等於KeyMarker參數值,但是UploadID比UploadIDMarker參數值大的分區上傳事件。

  • 使用預設參數

    ...
    lsRes, err := bucket.ListMultipartUploads(oss.KeyMarker(keyMarker), oss.UploadIDMarker(uploadIdMarker))
    if err != nil {
    log.Fatalf("failed to list multipart uploads: %w", err)
    }
    
    for _, upload := range lsRes.Uploads {
    log.Printf("Upload: %s, UploadID: %s\n", upload.Key, upload.UploadID)
    }
  • 指定首碼為file

    ...
    lsRes, err := bucket.ListMultipartUploads(oss.Prefix('file'))
    if err != nil {
    log.Fatalf("failed to list multipart uploads with prefix: %w", err)
    }
    
    log.Printf("Uploads:", lsRes.Uploads)
  • 指定最多返回100條結果資料

    ...
    lsRes, err := bucket.ListMultipartUploads(oss.MaxUploads(100))
    if err != nil {
    log.Fatalf("failed to list multipart uploads with limit: %w", err)
    }
    
    log.Printf("Uploads:", lsRes.Uploads)
  • 指定首碼為file且最多返回100條結果資料

    ...
    lsRes, err := bucket.ListMultipartUploads(oss.Prefix("file"), oss.MaxUploads(100))
    if err != nil {
    log.Fatalf("failed to list multipart uploads with prefix and limit: %w", err)
    }
    
    log.Printf("Uploads:", lsRes.Uploads)

相關文檔

  • 關於分區上傳的完整範例程式碼,請參見GitHub樣本

  • 分區上傳的完整實現涉及三個API介面,詳情如下:

  • 關於取消分區上傳事件的API介面說明,請參見AbortMultipartUpload

  • 關於列舉已上傳分區的API介面說明,請參見ListUploadedParts

  • 關於列舉所有執行中的分區上傳事件(即已初始化但尚未完成或已取消的分區上傳事件)的API介面說明,請參見ListMultipartUploads