本文介绍如何在开启版本控制的存储空间(Bucket)中拷贝文件(Object)。您可以通过CopyObject的方法拷贝小于1 GB的文件,通过分片拷贝(UploadPartCopy)的方法拷贝大于1 GB的文件。
注意事项
本文示例代码以华东1(杭州)的地域ID
cn-hangzhou
为例,默认使用外网Endpoint,如果您希望通过与OSS同地域的其他阿里云产品访问OSS,请使用内网Endpoint。关于OSS支持的Region与Endpoint的对应关系,请参见OSS地域和访问域名。本文以从环境变量读取访问凭证为例。如何配置访问凭证,请参见配置访问凭证。
要拷贝文件,您必须有
oss:GetObject
和oss:PutObject
权限。具体操作,请参见为RAM用户授权自定义的权限策略。
示例代码
拷贝对象
对于小于1 GB的文件,您可以通过CopyObject方法将文件从一个存储空间(源存储空间)复制到同一地域的另一个存储空间(目标存储空间)。
x-oss-copy-source默认拷贝Object的当前版本。如果当前版本是删除标记,则返回404表示该Object不存在。您可以在x-oss-copy-source中加入versionId来拷贝指定的Object版本,删除标记不能被拷贝。
您可以将Object的早期版本拷贝到同一个Bucket中,拷贝Object的历史版本将会成为一个新的当前版本,达到恢复Object早期版本的目的。
如果目标Bucket已开启版本控制,OSS将会为新拷贝出来的Object自动生成唯一的versionId,此versionId将会在响应header的x-oss-version-id中返回。如果目标Bucket未曾开启或者暂停了版本控制,OSS将会为新拷贝的Object自动生成versionId为”null“的版本,且会覆盖原先versionId为”null“的版本。
目标Bucket在开启或暂停版本控制状态下,不支持对Appendable类型Object执行拷贝操作。
您可以使用以下代码进行拷贝对象。
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 // 存储区域
srcBucketName string // 源存储空间名称
srcObjectName string // 源对象名称
destBucketName string // 目标存储空间名称
destObjectName string // 目标对象名称
)
// init函数用于初始化命令行参数
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&srcBucketName, "src-bucket", "", "The name of the source bucket.")
flag.StringVar(&srcObjectName, "src-object", "", "The name of the source object.")
flag.StringVar(&destBucketName, "dest-bucket", "", "The name of the destination bucket.")
flag.StringVar(&destObjectName, "dest-object", "", "The name of the destination object.")
}
func main() {
// 解析命令行参数
flag.Parse()
// 检查源bucket名称是否为空
if len(srcBucketName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, source bucket name required")
}
// 检查region是否为空
if len(region) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, region required")
}
// 如果目标bucket名称未指定,则使用源bucket名称
if len(destBucketName) == 0 {
destBucketName = srcBucketName
}
// 检查源object名称是否为空
if len(srcObjectName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, source object name required")
}
// 检查目标object名称是否为空
if len(destObjectName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, destination object name required")
}
// 加载默认配置并设置凭证提供者和区域
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
// 创建OSS客户端
client := oss.NewClient(cfg)
// 创建复制对象请求
copyRequest := &oss.CopyObjectRequest{
Bucket: oss.Ptr(destBucketName), // 目标存储空间名称
Key: oss.Ptr(destObjectName), // 目标对象名称
SourceKey: oss.Ptr(srcObjectName), // 源对象名称
SourceBucket: oss.Ptr(srcBucketName), // 源存储空间名称
SourceVersionId: oss.Ptr("yourVersionId"), // 填写实际的源对象的版本ID
StorageClass: oss.StorageClassStandard, // 设置存储类型为标准类型
TaggingDirective: oss.Ptr("Replace"), // 不拷贝源对象标签
Tagging: oss.Ptr("tag1=value1&tag2=value2"), // 填写实际的目标对象标签
}
// 执行复制对象操作并处理结果
copyResult, err := client.CopyObject(context.TODO(), copyRequest)
if err != nil {
log.Fatalf("failed to copy object: %v", err)
}
// 打印拷贝对象的versionId
log.Printf("copy object result versionId:%#v\n", *copyResult.VersionId)
}
分片拷贝
对于大于1GB的文件,需要使用分片拷贝(UploadPartCopy)。
UploadPartCopy默认从一个已存在的Object的当前版本中拷贝数据来上传一个Part。允许通过在UploadPartCopyRequest中附带SourceVersionId参数,实现从Object的指定版本进行拷贝。
如果未指定versionId且拷贝Object的当前版本为删除标记,OSS将返回404 Not Found。通过指定versionId来拷贝删除标记时,OSS将返回400 Bad Request。
您可以使用以下代码进行分片拷贝对象。
package main
import (
"context"
"flag"
"log"
"sync"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss"
"github.com/aliyun/alibabacloud-oss-go-sdk-v2/oss/credentials"
)
// 定义全局变量
var (
region string // 存储区域
srcBucketName string // 源存储空间名称
srcObjectName string // 源对象名称
destBucketName string // 目标存储空间名称
destObjectName string // 目标对象名称
)
// init函数用于初始化命令行参数
func init() {
flag.StringVar(®ion, "region", "", "The region in which the bucket is located.")
flag.StringVar(&srcBucketName, "src-bucket", "", "The name of the source bucket.")
flag.StringVar(&srcObjectName, "src-object", "", "The name of the source object.")
flag.StringVar(&destBucketName, "dest-bucket", "", "The name of the destination bucket.")
flag.StringVar(&destObjectName, "dest-object", "", "The name of the destination object.")
}
func main() {
// 解析命令行参数
flag.Parse()
// 定义上传ID
var uploadId string
// 检查源存储空间名称是否为空
if len(srcBucketName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, source bucket name required")
}
// 检查存储区域是否为空
if len(region) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, region required")
}
// 如果目标存储空间名称未指定,则使用源存储空间名称
if len(destBucketName) == 0 {
destBucketName = srcBucketName
}
// 检查源对象名称是否为空
if len(srcObjectName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, source object name required")
}
// 检查目标对象名称是否为空
if len(destObjectName) == 0 {
flag.PrintDefaults()
log.Fatalf("invalid parameters, destination object name required")
}
// 加载默认配置并设置凭证提供者和区域
cfg := oss.LoadDefaultConfig().
WithCredentialsProvider(credentials.NewEnvironmentVariableCredentialsProvider()).
WithRegion(region)
// 创建OSS客户端
client := oss.NewClient(cfg)
// 初始化分片上传请求
initRequest := &oss.InitiateMultipartUploadRequest{
Bucket: oss.Ptr(destBucketName),
Key: oss.Ptr(destObjectName),
}
initResult, err := client.InitiateMultipartUpload(context.TODO(), initRequest)
if err != nil {
log.Fatalf("failed to initiate multipart upload %v", err)
}
// 打印初始化分片上传的结果
log.Printf("initiate multipart upload result:%#v\n", *initResult.UploadId)
uploadId = *initResult.UploadId
// 初始化等待组和互斥锁
var wg sync.WaitGroup
var parts []oss.UploadPart
count := 3
var mu sync.Mutex
// 启动多个goroutine进行分片上传
for i := 0; i < count; i++ {
wg.Add(1)
go func(partNumber int, i int) {
defer wg.Done()
// 创建分片上传请求
partRequest := &oss.UploadPartCopyRequest{
Bucket: oss.Ptr(destBucketName), // 目标存储空间名称
Key: oss.Ptr(destObjectName), // 目标对象名称
SourceBucket: oss.Ptr(srcBucketName), // 源存储空间名称
SourceKey: oss.Ptr(srcObjectName), // 源对象名称
SourceVersionId: oss.Ptr("yourVersionId"), // 源对象版本ID
PartNumber: int32(partNumber), // 分片编号
UploadId: oss.Ptr(uploadId), // 上传ID
}
// 发送分片上传请求
partResult, err := client.UploadPartCopy(context.TODO(), partRequest)
if err != nil {
log.Fatalf("failed to upload part copy %d: %v", partNumber, err)
}
// 记录分片上传结果
part := oss.UploadPart{
PartNumber: partRequest.PartNumber,
ETag: partResult.ETag,
}
// 使用互斥锁保护共享数据
mu.Lock()
parts = append(parts, part)
mu.Unlock()
}(i+1, i)
}
// 等待所有goroutine完成
wg.Wait()
// 完成分片上传请求
request := &oss.CompleteMultipartUploadRequest{
Bucket: oss.Ptr(destBucketName),
Key: oss.Ptr(destObjectName),
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)
}
// 打印完成分片拷贝上传的versionId
log.Printf("complete multipart upload result versionId:%#v\n", *result.VersionId)
}
相关文档
关于拷贝对象的API接口,请参见CopyObject。
关于分片拷贝的API接口,请参见UploadPartCopy。