OSS提供的分片上传(Multipart Upload)功能,将要上传的较大文件(Object)分成多个分片(Part)来分别上传,上传完成后再调用CompleteMultipartUpload接口将这些Part组合成一个Object来达到断点续传的效果。
注意事项
- 本文以华东1(杭州)外网Endpoint为例。如果您希望通过与OSS同地域的其他阿里云产品访问OSS,请使用内网Endpoint。关于OSS支持的Region与Endpoint的对应关系,请参见访问域名和数据中心。
- 本文以OSS域名新建OSSClient为例。如果您希望通过自定义域名、STS等方式新建OSSClient,请参见Go初始化。
- 要分片上传,您必须有
oss:PutObject
权限。具体操作,请参见为RAM用户授权自定义的权限策略。
分片上传流程
分片上传(Multipart Upload)分为以下三个步骤:
- 初始化一个分片上传事件。
调用Bucket.InitiateMultipartUpload方法返回OSS创建的全局唯一的uploadID。
- 上传分片。
调用Bucket.UploadPart方法上传分片数据。
说明- 对于同一个uploadID,分片号(partNumber)标识了该分片在整个文件内的相对位置。如果使用同一个分片号上传了新的数据,那么OSS上该分片已有的数据将会被覆盖。
- OSS将收到的分片数据的MD5值放在ETag头内返回给用户。
- OSS计算上传数据的MD5值,并与SDK计算的MD5值比较,如果不一致则返回InvalidDigest错误码。
- 完成分片上传。
所有分片上传完成后,调用Bucket.CompleteMultipartUpload方法将所有分片合并成完整的文件。
分片上传完整示例
以下通过一个完整的示例对分片上传的流程进行逐步解析:
package main
import (
"fmt"
"os"
"time"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
)
func main() {
// 创建OSSClient实例。
// yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
client, err := oss.New("yourEndpoint", "yourAccessKeyId", "yourAccessKeySecret")
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// 填写存储空间名称。
bucketName := "examplebucket"
// 填写Object完整路径。Object完整路径中不能包含Bucket名称。
objectName := "exampleobject.txt"
// 填写本地文件的完整路径。如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。
locaFilename := "D:\\localpath\\examplefile.txt"
bucket, err := client.Bucket(bucketName)
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// 将本地文件分片,且分片数量指定为3。
chunks, err := oss.SplitFileByPartNum(locaFilename, 3)
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
fd, err := os.Open(locaFilename)
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
defer fd.Close()
// 指定过期时间。
expires := time.Date(2049, time.January, 10, 23, 0, 0, 0, time.UTC)
// 如果需要在初始化分片时设置请求头,请参考以下示例代码。
options := []oss.Option{
oss.MetadataDirective(oss.MetaReplace),
oss.Expires(expires),
// 指定该Object被下载时的网页缓存行为。
// oss.CacheControl("no-cache"),
// 指定该Object被下载时的名称。
// oss.ContentDisposition("attachment;filename=FileName.txt"),
// 指定该Object的内容编码格式。
// oss.ContentEncoding("gzip"),
// 指定对返回的Key进行编码,目前支持URL编码。
// oss.EncodingType("url"),
// 指定Object的存储类型。
// oss.ObjectStorageClass(oss.StorageStandard),
}
// 步骤1:初始化一个分片上传事件。
imur, err := bucket.InitiateMultipartUpload(objectName, options...)
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// 步骤2:上传分片。
var parts []oss.UploadPart
for _, chunk := range chunks {
fd.Seek(chunk.Offset, os.SEEK_SET)
// 调用UploadPart方法上传每个分片。
part, err := bucket.UploadPart(imur, fd, chunk.Size, chunk.Number)
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
parts = append(parts, part)
}
// 指定Object的读写权限为私有,默认为继承Bucket的读写权限。
objectAcl := oss.ObjectACL(oss.ACLPrivate)
// 步骤3:完成分片上传。
cmur, err := bucket.CompleteMultipartUpload(imur, parts, objectAcl)
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
fmt.Println("cmur:", cmur)
}
取消分片上传事件
您可以调用bucket.AbortMultipartUpload方法来取消分片上传事件。当一个分片上传事件被取消后,无法再使用同一个uploadID执行任何操作,已经上传的分片数据会被删除。
以下代码用于取消分片上传事件。
package main
import (
"fmt"
"os"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
)
func main() {
// 创建OSSClient实例。
// yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
client, err := oss.New("yourEndpoint", "yourAccessKeyId", "yourAccessKeySecret")
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// 填写Bucket名称。
bucket, err := client.Bucket("examplebucket")
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// 初始化一个分片上传事件。
// 填写Object完整路径。Object完整路径中不能包含Bucket名称。
imur, err := bucket.InitiateMultipartUpload("exampleobject.txt")
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// 取消分片上传。
err = bucket.AbortMultipartUpload(imur)
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
}
列举已上传的分片
以下代码用于列举某个分片上传事件中已经上传成功的分片:
package main
import (
"fmt"
"os"
"github.com/aliyun/aliyun-oss-go-sdk/oss"
)
func main() {
// 创建OSSClient实例。
// yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。
// 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。
client, err := oss.New("yourEndpoint", "yourAccessKeyId", "yourAccessKeySecret")
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// 填写Bucket名称。
bucketName := "examplebucket"
// 填写Object完整路径。Object完整路径中不能包含Bucket名称。
objectName := "exampleobject.txt"
// 填写本地文件的完整路径。如果未指定本地路径,则默认从示例程序所属项目对应本地路径中上传文件。
locaFilename := "D:\\localpath\\examplefile.txt"
bucket, err := client.Bucket(bucketName)
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// 将本地文件分片,且分片数量指定为3。
chunks, err := oss.SplitFileByPartNum(locaFilename, 3)
fd, err := os.Open(locaFilename)
defer fd.Close()
// 初始化一个分片上传事件。
imur, err := bucket.InitiateMultipartUpload(objectName)
uploadID := imur.UploadID
fmt.Println("InitiateMultipartUpload UploadID: ", uploadID)
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// 上传分片。
var parts []oss.UploadPart
for _, chunk := range chunks {
fd.Seek(chunk.Offset, os.SEEK_SET)
// 调用UploadPart方法上传每个分片。
part, err := bucket.UploadPart(imur, fd, chunk.Size, chunk.Number)
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
fmt.Println("UploadPartNumber: ", part.PartNumber, ", ETag: ", part.ETag)
parts = append(parts, part)
}
// 根据InitiateMultipartUploadResult列举已上传的分片。
lsRes, err := bucket.ListUploadedParts(imur)
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// 打印已上传的分片。
fmt.Println("\nParts:", lsRes.UploadedParts)
for _, upload := range lsRes.UploadedParts {
fmt.Println("List PartNumber: ", upload.PartNumber, ", ETag: " ,upload.ETag, ", LastModified: ", upload.LastModified)
}
// 根据objectName和uploadID生成InitiateMultipartUploadResult,然后列举所有已上传的分片。
var imur_with_uploadid oss.InitiateMultipartUploadResult
imur_with_uploadid.Key = objectName
imur_with_uploadid.UploadID = uploadID
// 列举已上传的分片。
lsRes, err = bucket.ListUploadedParts(imur_with_uploadid)
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
// 打印已上传的分片。
fmt.Println("\nListUploadedParts by UploadID: ", uploadID)
for _, upload := range lsRes.UploadedParts {
fmt.Println("List PartNumber: ", upload.PartNumber, ", ETag: " ,upload.ETag, ", LastModified: ", upload.LastModified)
}
// 完成分片上传。
cmur, err := bucket.CompleteMultipartUpload(imur, parts)
if err != nil {
fmt.Println("Error:", err)
os.Exit(-1)
}
fmt.Println("cmur:", cmur)
}
列举分片上传事件
您可以使用Bucket.ListMultipartUploads
方法列举所有执行中的分片上传事件,即已初始化但尚未完成或已取消的分片上传事件。可设置的参数请参见下表。
参数 | 说明 |
---|---|
Delimiter | 用于对Object名字进行分组的字符。所有名字包含指定的前缀且第一次出现Delimiter字符之间的Object作为一组元素。 |
MaxUploads | 限定此次返回分片上传事件的最大数目,默认值和最大值均为1000。 |
KeyMarker | 所有文件名称的字母序大于KeyMarker参数值的分片上传事件,可以与UploadIDMarker参数一同使用来指定返回结果的起始位置。 |
Prefix | 限定返回的文件名称必须以指定的Prefix作为前缀。注意使用Prefix查询时,返回的文件名称中仍会包含Prefix。 |
UploadIDMarker | 与KeyMarker参数一同使用来指定返回结果的起始位置。
|
- 使用默认参数
package main import ( "fmt" "os" "github.com/aliyun/aliyun-oss-go-sdk/oss" ) func main() { // 创建OSSClient实例。 // yourEndpoint填写Bucket所在地域对应的Endpoint。以华东1(杭州)为例,Endpoint填写为https://oss-cn-hangzhou.aliyuncs.com。 // 阿里云账号AccessKey拥有所有API的访问权限,风险很高。强烈建议您创建并使用RAM用户进行API访问或日常运维,请登录RAM控制台创建RAM用户。 client, err := oss.New("yourEndpoint", "yourAccessKeyId", "yourAccessKeySecret") if err != nil { fmt.Println("Error:", err) os.Exit(-1) } // 填写Bucket名称。 bucketName := "examplebucket" bucket, err := client.Bucket(bucketName) if err != nil { fmt.Println("Error:", err) os.Exit(-1) } // 列举所有分片上传事件。 keyMarker := "" uploadIdMarker := "" for { // 默认情况一次最多返回1000条记录。 lsRes, err := bucket.ListMultipartUploads(oss.KeyMarker(keyMarker), oss.UploadIDMarker(uploadIdMarker)) if err != nil { fmt.Println("Error:", err) os.Exit(-1) } // 打印分片上传事件。 for _, upload := range lsRes.Uploads { fmt.Println("Upload: ", upload.Key, ", UploadID: ",upload.UploadID) } if lsRes.IsTruncated { keyMarker = lsRes.NextKeyMarker uploadIdMarker = lsRes.NextUploadIDMarker } else { break } } }
- 指定前缀为file
lsRes, err := bucket.ListMultipartUploads(oss.Prefix("file")) if err != nil { fmt.Println("Error:", err) os.Exit(-1) } fmt.Println("Uploads:", lsRes.Uploads)
- 指定最多返回100条结果数据
lsRes, err := bucket.ListMultipartUploads(oss.MaxUploads(100)) if err != nil { fmt.Println("Error:", err) os.Exit(-1) } fmt.Println("Uploads:", lsRes.Uploads)
- 指定前缀为file且最多返回100条结果数据
lsRes, err := bucket.ListMultipartUploads(oss.Prefix("file"), oss.MaxUploads(100)) if err != nil { fmt.Println("Error:", err) os.Exit(-1) } fmt.Println("Uploads:", lsRes.Uploads)
相关文档
- 关于分片上传的完整示例代码,请参见GitHub示例。
- 分片上传的完整实现涉及三个API接口,详情如下:
- 关于初始化分片上传事件的API接口说明,请参见InitiateMultipartUpload。
- 关于分片上传Part的API接口说明,请参见UploadPart。
- 关于完成分片上传的API接口说明,请参见CompleteMultipartUpload。
- 关于取消分片上传事件的API接口说明,请参见AbortMultipartUpload。
- 关于列举已上传分片的API接口说明,请参见ListParts。
- 关于列举所有执行中的分片上传事件(即已初始化但尚未完成或已取消的分片上传事件)的API接口说明,请参见ListMultipartUploads。