This topic describes how to copy small objects and large objects from a versioning-enabled bucket.

Copy a small object

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.

The following code provides an example on how to copy objects smaller than 1 GB:

using Aliyun.OSS;
using Aliyun.OSS.Common;
var endpoint = "<yourEndpoint>";
var accessKeyId = "<yourAccessKeyId>";
var accessKeySecret = "<yourAccessKeySecret>";
var sourceBucket = "<yourSourceBucketName>";
var sourceObject = "<yourSourceObjectName>";
var targetBucket = "<yourDestBucketName>";
var targetObject = "<yourDestObjectName>";
var versionid = "<yourArchiveObjectVersionid>";
// Create an OSSClient instance.
var client = new OssClient(endpoint, accessKeyId, accessKeySecret);
try
{
    var metadata = new ObjectMetadata();
    metadata.AddHeader("mk1", "mv1");
    metadata.AddHeader("mk2", "mv2");
    var req = new CopyObjectRequest(sourceBucket, sourceObject, targetBucket, targetObject)
    {
        // If the value of NewObjectMetadata is null, the COPY mode is used and the metadata of the source object is copied to the destination object. If the value of NewObjectMetadata is not null, the REPLACE mode is used and the metadata of the source object overwrites that of the destination object.
        NewObjectMetadata = metadata, 
        // Specify the version ID of the object.
        SourceVersionId = versionid
    };
    // Copy the object.
    var result = client.CopyObject(req);
    Console.WriteLine("Copy object succeeded, vesionid:{0}", result.VersionId);
}
catch (OssException ex)
{
    Console.WriteLine("Failed with error code: {0}; Error info: {1}. \nRequestID: {2} \tHostID: {3}",
        ex.ErrorCode, ex.Message, ex.RequestId, ex.HostId);
}
catch (Exception ex)
{
    Console.WriteLine("Failed with error info: {0}", ex.Message);
}

Copy a large object

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.

The following code provides an example for multipart copy:

using Aliyun.OSS;
var endpoint = "<yourEndpoint>";
var accessKeyId = "<yourAccessKeyId>";
var accessKeySecret = "<yourAccessKeySecret>";
var sourceBucket = "<yourSourceBucketName>";
var sourceObject = "<yourSourceObjectName>";
var targetBucket = "<yourDestBucketName>";
var targetObject = "<yourDestObjectName>";
var sourceObjectVersionid = "<yourSourceObjectVersionid>";
var uploadId = "";
var partSize = 50 * 1024 * 1024;
// Create an OSSClient instance.
var client = new OssClient(endpoint, accessKeyId, accessKeySecret);
try
{
    // Initialize a multipart copy task. You can call the InitiateMultipartUploadRequest operation to specify the metadata of the destination object.
    var request = new InitiateMultipartUploadRequest(targetBucket, targetObject);
    var result = client.InitiateMultipartUpload(request);
    // Display the upload ID.
    uploadId = result.UploadId;
    Console.WriteLine("Init multipart upload succeeded, Upload Id: {0}", result.UploadId);
    // Calculate the total number of parts.
    var request = new GetObjectMetadataRequest(sourceBucket, sourceObject)
    {
        // Specify the version ID of the object.
        VersionId = sourceObjectVersionid
    };
    var metadata = client.GetObjectMetadata(request);
    var fileSize = metadata.ContentLength;
    var partCount = (int)fileSize / partSize;
    if (fileSize % partSize ! = 0)
    {
        partCount++;
    }
    // Start the multipart copy task.
    var partETags = new List<PartETag>();
    for (var i = 0; i < partCount; i++)
    {
        var skipBytes = (long)partSize * i;
        var size = (partSize < fileSize - skipBytes) ? partSize : (fileSize - skipBytes);
        // Create a UploadPartCopyRequest request. You can call the UploadPartCopyRequest operation to specify conditions.
        var uploadPartCopyRequest = new UploadPartCopyRequest(targetBucket, targetObject, sourceBucket, sourceObject, uploadId)
            {
                PartSize = size,
                PartNumber = i + 1,
                // Use BeginIndex to find the start position to copy the part.
                BeginIndex = skipBytes,
                // Specify the version ID of the object.
                VersionId = sourceObjectVersionid
            };
        // Call the uploadPartCopy method to copy each part.
        var uploadPartCopyResult = client.UploadPartCopy(uploadPartCopyRequest);
        Console.WriteLine("UploadPartCopy : {0}", i);
        partETags.Add(uploadPartCopyResult.PartETag);
    }
    // Complete the multipart copy task.
    var completeMultipartUploadRequest =
    new CompleteMultipartUploadRequest(targetBucket, targetObject, uploadId);
    // partETags is a list of part ETags. OSS verifies the validity of each part after it receives the part list. After all the parts are verified, OSS combines these parts into a complete object.
    foreach (var partETag in partETags)
    {
        completeMultipartUploadRequest.PartETags.Add(partETag);
    }
    var completeMultipartUploadResult = client.CompleteMultipartUpload(completeMultipartUploadRequest);
    Console.WriteLine("CompleteMultipartUpload succeeded, vesionid:{0}", completeMultipartUploadResult.VersionId);
}
catch (OssException ex)
{
    Console.WriteLine("Failed with error code: {0}; Error info: {1}. \nRequestID: {2} \tHostID: {3}",
        ex.ErrorCode, ex.Message, ex.RequestId, ex.HostId);
}
catch (Exception ex)
{
    Console.WriteLine("Failed with error info: {0}", ex.Message);
}

For more information about UploadPartCopy, see UploadPartCopy.