本文介绍如何在受版本控制的存储空间(Bucket)中拷贝文件(Object)。您可以通过CopyObject的方法拷贝小于1 GB的文件,通过分片拷贝(UploadPartCopy)的方法拷贝大于1 GB的文件。
注意事项
本文以华东1(杭州)外网Endpoint为例。如果您希望通过与OSS同地域的其他阿里云产品访问OSS,请使用内网Endpoint。关于OSS支持的Region与Endpoint的对应关系,请参见OSS访问域名、数据中心、开放端口。
本文以从环境变量读取访问凭证为例。如何配置访问凭证,请参见配置访问凭证。
本文以OSS域名新建OSSClient为例。如果您希望通过自定义域名、STS等方式新建OSSClient,请参见初始化。
要拷贝文件,您必须有
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 (
"log"
"net/http"
"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("Failed to create credentials provider: %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("Failed to create OSS client: %v", err)
}
// yourBucketName填写存储空间名称。
bucketName := "yourBucketName"
// yourObjectName填写不包含Bucket名称在内的源Object的完整路径。
objectName := "yourObjectName"
// yourDestObjectName填写不包含Bucket名称在内的目标Object的完整路径。
destObjectName := "yourDestObjectName"
// 获取Bucket实例。
bucket, err := client.Bucket(bucketName)
if err != nil {
log.Fatalf("Failed to get bucket '%s': %v", bucketName, err)
}
// 拷贝指定版本文件到同一个存储空间的另一个文件,并获取版本信息。
var retHeader http.Header
_, err = bucket.CopyObject(objectName, destObjectName, oss.VersionId("yourObjectVersionId"), oss.GetResponseHeader(&retHeader))
if err != nil {
log.Fatalf("Failed to copy object '%s' to '%s': %v", objectName, destObjectName, err)
}
// 打印版本信息。
versionId := oss.GetVersionId(retHeader)
copySourceVersionId := oss.GetCopySrcVersionId(retHeader)
log.Printf("x-oss-version-id: %s", versionId)
log.Printf("x-oss-copy-source-version-id: %s", copySourceVersionId)
}
拷贝大文件
对于大于1GB的文件,需要使用分片拷贝(UploadPartCopy)。
UploadPartCopy默认从一个已存在的Object的当前版本中拷贝数据来上传一个Part。允许通过在请求header : x-oss-copy-source中附带versionId的子条件,实现从Object的指定版本进行拷贝,如x-oss-copy-source : /SourceBucketName/SourceObjectName?versionId=111111。
SourceObjectName要进行URL编码。响应中将会返回被拷贝Object的版本id:x-oss-copy-source-version-id。
如果未指定versionId且拷贝Object的当前版本为删除标记,OSS将返回404 Not Found。通过指定versionId来拷贝删除标记时,OSS将返回400 Bad Request。
以下代码用于分片拷贝:
package main
import (
"log"
"net/http"
"time"
"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("Failed to create credentials provider: %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("Failed to create OSS client: %v", err)
}
// yourBucketName填写存储空间名称。
bucketName := "yourBucketName"
bucket, err := client.Bucket(bucketName)
if err != nil {
log.Fatalf("Failed to get bucket '%s': %v", bucketName, err)
}
// yourObjectName填写不包含Bucket名称在内的Object的完整路径。
objectName := "yourObjectName"
fileName := "yourFileName"
futureDate := time.Date(2049, time.January, 10, 23, 0, 0, 0, time.UTC)
var retHeader http.Header
// 分割文件。
chunks, err := oss.SplitFileByPartNum(fileName, 3)
if err != nil {
log.Fatalf("Failed to split file '%s': %v", fileName, err)
}
// 上传一个待拷贝的大文件。
err = bucket.PutObjectFromFile(objectName, fileName, oss.GetResponseHeader(&retHeader))
if err != nil {
log.Fatalf("Failed to put object '%s' from file '%s': %v", objectName, fileName, err)
}
// 取得上传源文件的versionId。
versionId := oss.GetVersionId(retHeader)
log.Printf("Source object version ID: %s", versionId)
// 设置拷贝文件参数。
options := []oss.Option{
oss.Expires(futureDate),
oss.Meta("my", "myprop"),
}
// 目标文件名。
objectDest := objectName + "dest"
var parts []oss.UploadPart
copyOptions := []oss.Option{
oss.VersionId(versionId),
oss.GetResponseHeader(&retHeader),
}
// 初始化一个分片上传事件。
imu, err := bucket.InitiateMultipartUpload(objectDest, options...)
if err != nil {
log.Fatalf("Failed to initiate multipart upload for object '%s': %v", objectDest, err)
}
// 进行大文件拷贝。
for _, chunk := range chunks {
part, err := bucket.UploadPartCopy(imu, bucketName, objectName, chunk.Offset, chunk.Size, int(chunk.Number), copyOptions...)
if err != nil {
log.Fatalf("Failed to upload part %d of object '%s': %v", chunk.Number, objectName, err)
}
parts = append(parts, part)
// 打印x-oss-copy-source-version-id。
log.Printf("x-oss-copy-source-version-id: %s", oss.GetCopySrcVersionId(retHeader))
}
// 完成分片上传。
_, err = bucket.CompleteMultipartUpload(imu, parts, oss.GetResponseHeader(&retHeader))
if err != nil {
log.Fatalf("Failed to complete multipart upload for object '%s': %v", objectDest, err)
}
// 打印x-oss-version-id。
log.Printf("x-oss-version-id: %s", oss.GetVersionId(retHeader))
}
相关文档
关于拷贝小文件的API接口说明,请参见CopyObject。
关于拷贝大文件的API接口说明,请参见UploadPartCopy。