All Products
Search
Document Center

Object Storage Service:Copy objects (C# SDK V1)

Last Updated:Mar 20, 2026

Copy objects in a versioning-enabled OSS bucket using CopyObject (objects up to 1 GB) or UploadPartCopy with multipart copy (objects larger than 1 GB).

Prerequisites

Before you begin, ensure that you have:

  • An OSS bucket with versioning enabled

  • The oss:GetObject and oss:PutObject permissions on the source and destination buckets. For details, see Attach a custom policy to a RAM user

  • The OSS_ACCESS_KEY_ID and OSS_ACCESS_KEY_SECRET environment variables set with your AccessKey credentials

Usage notes

  • The examples in this topic use the public endpoint for the China (Hangzhou) region. To access OSS from another Alibaba Cloud service in the same region, use an internal endpoint instead. For a full list of endpoints, see Regions and endpoints.

  • The code examples create an OssClient instance using an OSS endpoint. To create an instance using a custom domain name or Security Token Service (STS), see Initialization.

Copy a small object

Use CopyObject to copy an object up to 1 GB in size between buckets in the same region.

How versioning affects the copy operation:

  • By default, CopyObject copies the current version of the source object. If the current version is a delete marker, OSS returns HTTP 404 (object not found). To copy a specific version, include versionId in the x-oss-copy-source request header.

  • Delete markers cannot be copied.

  • Copying a previous version to the same bucket promotes it to the current version, effectively restoring it.

  • If the destination bucket has versioning enabled, OSS assigns a unique version ID to the destination object and returns it in the x-oss-version-id response header.

  • If the destination bucket has versioning disabled or suspended, OSS creates a version with ID null and overwrites any existing null version.

  • Appendable objects cannot be copied to a bucket with versioning enabled or suspended.

Metadata copy behavior:

  • Set NewObjectMetadata to null to use COPY mode — the source object's metadata is copied to the destination object.

  • Set NewObjectMetadata to a non-null value to use REPLACE mode — the specified metadata replaces the source object's metadata.

using Aliyun.OSS;
using Aliyun.OSS.Common;

// Specify the endpoint for the region where your bucket is located.
// Example: https://oss-cn-hangzhou.aliyuncs.com for China (Hangzhou).
var endpoint = "yourEndpoint";

// Read AccessKey credentials from environment variables.
var accessKeyId = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_ID");
var accessKeySecret = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_SECRET");

// Source bucket and object.
var sourceBucket = "yourSourceBucketName";
var sourceObject = "yourSourceObjectName";    // Full path, excluding the bucket name.

// Destination bucket and object. The destination bucket must be in the same region.
var targetBucket = "yourDestBucketName";
var targetObject = "yourDestObjectName";      // Full path, excluding the bucket name.

// Version ID of the source object to copy.
var versionid = "yourArchiveObjectVersionid";

// Region identifier, e.g., cn-hangzhou for China (Hangzhou).
const string region = "cn-hangzhou";

// Configure the client to use signature algorithm V4.
var conf = new ClientConfiguration();
conf.SignatureVersion = SignatureVersion.V4;

// Create the OssClient instance.
var client = new OssClient(endpoint, accessKeyId, accessKeySecret, conf);
client.SetRegion(region);

try
{
    // Define destination object metadata (REPLACE mode).
    // Set NewObjectMetadata to null to copy source metadata instead (COPY mode).
    var metadata = new ObjectMetadata();
    metadata.AddHeader("mk1", "mv1");
    metadata.AddHeader("mk2", "mv2");

    var req = new CopyObjectRequest(sourceBucket, sourceObject, targetBucket, targetObject)
    {
        NewObjectMetadata = metadata,
        SourceVersionId = versionid
    };

    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

For objects larger than 1 GB, use multipart copy with UploadPartCopy. The operation splits the source object into parts and copies each part independently.

How versioning affects the copy operation:

  • By default, UploadPartCopy copies the current version of the source object. To copy a specific version, add the version ID to the x-oss-copy-source request header. For example: x-oss-copy-source: /SourceBucketName/SourceObjectName?versionId=CAEQMxiBgICAof2D0BYiIDJhMGE3N2M1YTI1NDQzOGY5NTkyNTI3MGYyMzJm****

  • The SourceObjectName parameter must be URL-encoded.

  • The version ID of the copied object is returned in the x-oss-copy-source-version-id response header.

  • If no version ID is specified and the current version is a delete marker, OSS returns 404 Not Found.

  • If a version ID is specified and that version is a delete marker, OSS returns 400 Bad Request.

Multipart copy steps:

  1. Initiate — call InitiateMultipartUpload to start the task and get an upload ID.

  2. Get metadata — call GetObjectMetadata to retrieve the source object size.

  3. Split and copy — divide the object into 50 MB parts and call UploadPartCopy for each part, collecting the returned PartETag values.

  4. Complete — call CompleteMultipartUpload with the collected PartETag list to assemble the final object.

using System;
using System.Collections.Generic;
using Aliyun.OSS;
using Aliyun.OSS.Common;

namespace Samples
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var endpoint = "https://oss-cn-hangzhou.aliyuncs.com";

            // Read AccessKey credentials from environment variables.
            var accessKeyId = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_ID");
            var accessKeySecret = Environment.GetEnvironmentVariable("OSS_ACCESS_KEY_SECRET");

            // Source bucket and object.
            var sourceBucket = "yourSourceBucketName";
            var sourceObject = "yourSourceObjectName";    // Full path, excluding the bucket name.

            // Destination bucket and object. Must be in the same region as the source.
            var targetBucket = "yourDestBucketName";
            var targetObject = "yourDestObjectName";      // Full path, excluding the bucket name.

            // Version ID of the source object to copy.
            var sourceObjectVersionid = "yourSourceObjectVersionid";

            var partSize = 50 * 1024 * 1024;  // 50 MB per part.

            var client = new OssClient(endpoint, accessKeyId, accessKeySecret);

            try
            {
                // Step 1: Initiate the multipart copy task.
                var initRequest = new InitiateMultipartUploadRequest(targetBucket, targetObject);
                var result = client.InitiateMultipartUpload(initRequest);
                var uploadId = result.UploadId;
                Console.WriteLine("Init multipart upload succeeded, Upload Id: {0}", uploadId);

                // Step 2: Retrieve the source object size.
                var metadataRequest = new GetObjectMetadataRequest(sourceBucket, sourceObject)
                {
                    VersionId = sourceObjectVersionid
                };
                var metadata = client.GetObjectMetadata(metadataRequest);
                var fileSize = metadata.ContentLength;

                // Calculate the number of parts.
                var partCount = (int)fileSize / partSize;
                if (fileSize % partSize != 0)
                {
                    partCount++;
                }

                // Step 3: Copy each part.
                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);

                    var uploadPartCopyRequest = new UploadPartCopyRequest(
                        targetBucket, targetObject, sourceBucket, sourceObject, uploadId)
                    {
                        PartSize = size,
                        PartNumber = i + 1,
                        BeginIndex = skipBytes,       // Start byte position for this part.
                        VersionId = sourceObjectVersionid
                    };

                    var uploadPartCopyResult = client.UploadPartCopy(uploadPartCopyRequest);
                    Console.WriteLine("UploadPartCopy : {0}", i);
                    partETags.Add(uploadPartCopyResult.PartETag);
                }

                // Step 4: Complete the multipart copy.
                // OSS verifies each part against its ETag, then assembles the final object.
                var completeRequest = new CompleteMultipartUploadRequest(targetBucket, targetObject, uploadId);
                foreach (var partETag in partETags)
                {
                    completeRequest.PartETags.Add(partETag);
                }

                var completeResult = client.CompleteMultipartUpload(completeRequest);
                Console.WriteLine("CompleteMultipartUpload succeeded, vesionid:{0}", completeResult.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);
            }
        }
    }
}

References

  • CopyObject — API reference for copying small objects

  • UploadPartCopy — API reference for copying large objects using multipart copy