This topic describes how to copy objects in a versioned bucket. You can use CopyObject to copy an object that is up to 1 GB in size and use UploadPartCopy to copy an object larger than 1 GB.

Copy a small object

You can use CopyObject to copy an object that is up to 1 GB in size from a source bucket to a destination bucket within the same region.

  • By default, the x-oss-copy-source header in a CopyObject request specifies the current version of the object to copy. If the current version of the object to copy is a delete marker, 404 Not Found is returned to indicate that the object does not exist. You can add a version ID in the x-oss-copy-source header to copy a specified object version. Delete markers cannot be copied.
  • You can copy a previous version of an object to the same bucket. The copied previous version becomes the current version of the object. This way, you can restore an object to a specified previous version.
  • If versioning is enabled for the destination bucket, OSS generates a unique version ID for the destination object. The version ID is returned in the response as the x-oss-version-id header value. If versioning is disabled or suspended for the destination bucket, OSS generates a version whose version ID is null for the destination object and overwrites the original version whose version ID is null.
  • Append objects in a bucket with versioning enabled or suspended cannot be copied.
The following code provides an example on how to copy a small object:
package main

import (
  "fmt"
  "os"
  "github.com/aliyun/aliyun-oss-go-sdk/oss"
)

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

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

  // Specify the name of the bucket.
  bucket, err := client.Bucket(bucketName)
  if err ! = nil {
    fmt.Println("Error:", err)
    os.Exit(-1)
  }

  // Copy the specified version of the object to another object in the same bucket and query the version ID of the destination object.
  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)
  }

  // Display the version ID of the destination object.
  fmt.Println("x-oss-version-id:", oss.GetVersionId(retHeader))
  fmt.Println("x-oss-copy-source-version-id:", oss.GetCopySrcVersionId(retHeader))
}

For more information about how to copy small objects, see CopyObject.

Copy a large object

To copy an object larger than 1 GB, you must split the object into parts and copy them sequentially by using UploadPartCopy.

By default, the UploadPartCopy operation uploads a part by copying data from the current version of an existing object. You can add versionId in the request header x-oss-copy-source as the condition to upload a part by copying data from a specified version of an existing object. Example: x-oss-copy-source : /SourceBucketName/SourceObjectName?versionId=111111.
Note The value of SourceObjectName must be URL-encoded. The version ID of the copied object version is returned as the x-oss-copy-source-version-id header value in the response.

If versionId is not specified in the request and the current version of the source object is a delete marker, OSS returns 404 Not Found. If versionId is specified in the request and the specified version of the source object is a delete marker, OSS returns 400 Bad Request.

The following code provides an example on how to use multipart copy to copy large objects:
package main

import (
  "fmt"
  "net/http"
  "os"
  "time"

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

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

  // Specify the name of the bucket.
  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)

  // Upload a large object as the source object.
  err = bucket.PutObjectFromFile(objectName, fileName, oss.GetResponseHeader(&retHeader))
  if err ! = nil {
    fmt.Println("Error:", err)
    os.Exit(-1)
  }
  // Query the version ID of the uploaded source object.
  versionId := oss.GetVersionId(retHeader)

  options := []oss.Option{
    oss.Expires(futureDate), oss.Meta("my", "myprop"),
  }
  // Specify the name of the destination object.
  objectDest := objectName + "dest"
  var parts []oss.UploadPart
  // Set parameters for object copy. 
  copyOptions := []oss.Option{
     oss.VersionId(versionId), oss.GetResponseHeader(&retHeader),
  }

  // Copy the large object.
  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)

    // Display the value of the x-oss-copy-source-version-id header.
    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)
  }

  // Display the value of x-oss-version-id.
  fmt.Println("x-oss-version-id:", oss.GetVersionId(retHeader))
}

For more information about multipart copy, see UploadPartCopy.