This topic describes how to copy objects in a bucket with versioning enabled or suspended.

Copy small objects

You can use the CopyObject method to copy an object smaller than 1 GB from a bucket (source bucket) to another bucket (target bucket) in the same region.

Note
  • CopyObject copies the current version of an object by default. If the current version of the target object is a delete marker, the 404 Not Found error is returned to indicate that the object does not exist. You can specify the versionId in a CopyObject request to copy a specified version of the target object. However, a delete marker cannot be copied
  • You can copy a historical version of an object to the bucket that stores the object. The copied historical version becomes the current version of a new object. You can use this method to restore a historical version of an object.
  • If versioning is enabled for the bucket that stores the target object, OSS generates a unique version ID for the copied object, which is returned in the x-oss-version-id field in the response header. If versioning is suspended or not enabled for the bucket that stores the target object, OSS generates a version of which the ID is null for the copied object and overwrite overwrites the original version of which the ID is null.
  • If versioning is enabled or suspended for a bucket, you cannot perform the CopyObject operation on appendable objects in the bucket.
You can run the following code to copy objects smaller than 1 GB:
package main

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

func main() {
  // Creates 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>"

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

  // Copies a specified version of the object to another object in the same bucket, and obtains the version ID of the newly copied 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)
  }

  // Prints the version ID of the target 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 copying objects smaller than 1 GB, see CopyObject.

Copy large objects

To copy an object larger than 1 GB, you must use UploadPartCopy.

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

If you do not specify the versionId in the request header and the current version of the object to be copied is a delete marker, the 404 Not Found error is returned. If you specify the versionId in the request header and the specified version of the object is a delete marker, the 400 Bad Request error is returned.

You can run the following code to copy an object by using UploadPartCopy:
package main

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

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

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

  // Obtains 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)

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

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

  // Copies 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)

    // Prints 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)
  }

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

For more information about UploadPartCopy, see UploadPartCopy.