本文介绍如何在受版本控制的存储空间(Bucket)中拷贝文件(Object)。

拷贝小文件

对于小于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 (
  "fmt"
  "os"
  "github.com/aliyun/aliyun-oss-go-sdk/oss"
)

func main() {
  // 创建OSSClient实例。
  client, err := oss.New("<yourEndpoint>", "<yourAccessKeyId>", "<yourAccessKeySecret>")
  if err != nil {
    fmt.Println("Error:", err)
    os.Exit(-1)
  }

  bucketName := "<yourBucketName>"
  objectName := "<yourObjectName>"
  destObjectName := "<yourDestObjectName>"

  // 获取存储空间。
  bucket, err := client.Bucket(bucketName)
  if err != nil {
    fmt.Println("Error:", err)
    os.Exit(-1)
  }

  // 拷贝指定版本文件到同一个存储空间的另一个文件,并获取版本信息。
  var retHeader http.Header
  _, err = bucket.CopyObject(objectName, destObjectName, oss.VersionId("yourObjectVersionId"), oss.GetResponseHeader(&retHeader))
  if err != nil {
    fmt.Println("Error:", err)
    os.Exit(-1)
  }

  // 打印版本信息。
  fmt.Println("x-oss-version-id:", oss.GetVersionId(retHeader))
  fmt.Println("x-oss-copy-source-version-id:", oss.GetCopySrcVersionId(retHeader))
}

拷贝小文件的详细说明请参见CopyObject

拷贝大文件

对于大于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 (
  "fmt"
  "net/http"
  "os"
  "time"

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

func main() {
  // 创建OSSClient实例。
  client, err := oss.New("<yourEndpoint>", "<yourAccessKeyId>", "<yourAccessKeySecret>")
  if err != nil {
    fmt.Println("Error:", err)
    os.Exit(-1)
  }

  // 获取存储空间。
  bucket, err := client.Bucket("yourBucketName")
  if err != nil {
    fmt.Println("Error:", err)
    os.Exit(-1)
  }

  bucketName := "yourBucketName"
  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)

  // 上传一个待拷贝大文件。
  err = bucket.PutObjectFromFile(objectName, fileName, oss.GetResponseHeader(&retHeader))
  if err != nil {
    fmt.Println("Error:", err)
    os.Exit(-1)
  }
  // 取得上传源文件的versionId。
  versionId := oss.GetVersionId(retHeader)

  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...)
  for _, chunk := range chunks {
    part, err := bucket.UploadPartCopy(imu, bucketName, objectName, chunk.Offset, chunk.Size, (int)(chunk.Number), copyOptions...)
    if err != nil {
      fmt.Println("Error:", err)
      os.Exit(-1)
    }
    parts = append(parts, part)

    // 打印x-oss-copy-source-version-id。
    fmt.Println("x-oss-copy-source-version-id:", oss.GetCopySrcVersionId(retHeader))
  }
  _, err = bucket.CompleteMultipartUpload(imu, parts, oss.GetResponseHeader(&retHeader))
  if err != nil {
    fmt.Println("Error:", err)
    os.Exit(-1)
  }

  // 打印x-oss-version-id。
  fmt.Println("x-oss-version-id:", oss.GetVersionId(retHeader))
}

分片拷贝的详细信息请参见UploadPartCopy