Object Storage Service (OSS) allows you to configure object tags to classify objects. You can configure lifecycle rules and control access to objects based on tags.

Note For more information about object tags, see Object tagging.

Add tags to an object when you upload the object

The following examples describe how to add tags to objects in simple upload, multipart upload, append upload, and resumable upload:

  • Add tags to an object when you upload the object by using simple upload

    The following code provides an example on how to add tags to an object when you upload the object by using simple upload:

    using System.Text;
    using Aliyun.OSS;
    using System.Text;
    using Aliyun.OSS.Util;
    
    // Set yourEndpoint to the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. 
    var endpoint = "yourEndpoint";
    // The AccessKey pair of an Alibaba Cloud account has permissions on all API operations. Using these credentials to access OSS is a high-risk operation. We recommend that you use a RAM user to call API operations or perform routine O&M. To create a RAM user, log on to the RAM console. 
    var accessKeyId = "yourAccessKeyId";
    var accessKeySecret = "yourAccessKeySecret";
    // Specify the bucket name. 
    var bucketName = "examplebucket";
    // Specify the full path of the object. The full path of the object cannot contain the bucket name. 
    var objectName = "exampleobject.txt";
    var objectContent = "More than just cloud.";
    
    String UrlEncodeKey(String key)
    {
    const string CharsetName = "utf-8";
    const char separator = '/';
    var segments = key.Split(separator);
    
    var encodedKey = new StringBuilder();
    encodedKey.Append(HttpUtils.EncodeUri(segments[0], CharsetName));
    for (var i = 1; i < segments.Length; i++)
        encodedKey.Append(separator).Append(HttpUtils.EncodeUri(segments[i], CharsetName));
    
        if (key.EndsWith(separator.ToString()))
        {
            // String#split ignores trailing empty strings, e.g., "a/b/" will be split as a 2-entries array,
            // so we have to append all the trailing slash to the uri.
            foreach (var ch in key)
            {
                if (ch == separator)
                    encodedKey.Append(separator);
                else
                    break;
            }
        }
    
    return encodedKey.ToString();
    }
    // Create an OSSClient instance. 
    var client = new OssClient(endpoint, accessKeyId, accessKeySecret);
    try
    {
        byte[] binaryData = Encoding.ASCII.GetBytes(objectContent);
        MemoryStream requestContent = new MemoryStream(binaryData);
    
        var meta = new ObjectMetadata();
        // Configure the tags in the HTTP header. 
        string str = UrlEncodeKey("key1") + "=" + UrlEncodeKey("key1") + "&" + UrlEncodeKey("key2") + "=" + UrlEncodeKey("key2");
        meta.AddHeader("x-oss-tagging", str);
        var putRequest = new PutObjectRequest(bucketName, objectName, requestContent);
        putRequest.Metadata = meta;
    
        // Upload the object and add tags to the object. 
        client.PutObject(putRequest);
        Console.WriteLine("Put object succeeded");
    }
    catch (Exception ex)
    {
        Console.WriteLine("Put object failed, {0}", ex.Message);
    }
  • Add tags to an object when you upload the object by using multipart upload

    The following code provides an example on how to add tags to an object when you upload it by using multipart upload:

    using Aliyun.OSS;
    using System.Text;
    
    // Set yourEndpoint to the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. 
    var endpoint = "yourEndpoint";
    // The AccessKey pair of an Alibaba Cloud account has permissions on all API operations. Using these credentials to access OSS is a high-risk operation. We recommend that you use a RAM user to call API operations or perform routine O&M. To create a RAM user, log on to the RAM console. 
    var accessKeyId = "yourAccessKeyId";
    var accessKeySecret = "yourAccessKeySecret";
    // Specify the bucket name. 
    var bucketName = "examplebucket";
    // Specify the full path of the object. The full path of the object cannot contain the bucket name. 
    var objectName = "exampleobject.txt";
    // Specify the full path of the local file. Example: D:\\localpath\\examplefile.txt. By default, if you do not specify the local file, the local file is uploaded from the path of the project to which the sample program belongs. 
    var localFilename = "D:\\localpath\\examplefile.txt";
    
    String UrlEncodeKey(String key)
    {
    const string CharsetName = "utf-8";
    const char separator = '/';
    var segments = key.Split(separator);
    
    var encodedKey = new StringBuilder();
    encodedKey.Append(HttpUtils.EncodeUri(segments[0], CharsetName));
    for (var i = 1; i < segments.Length; i++)
        encodedKey.Append(separator).Append(HttpUtils.EncodeUri(segments[i], CharsetName));
    
        if (key.EndsWith(separator.ToString()))
        {
            // String#split ignores trailing empty strings, e.g., "a/b/" will be split as a 2-entries array,
            // so we have to append all the trailing slash to the uri.
            foreach (var ch in key)
            {
                if (ch == separator)
                    encodedKey.Append(separator);
                else
                    break;
            }
        }
    
    return encodedKey.ToString();
    }
    
    // Create an OSSClient instance. 
    var client = new OssClient(endpoint, accessKeyId, accessKeySecret);
    // Initiate a multipart upload task. 
    var uploadId = "";
    try
    {
        // Specify the name of the object and the bucket to which the object is uploaded. You can configure object metadata in InitiateMultipartUploadRequest, but you do not need to specify ContentLength. 
        var request = new InitiateMultipartUploadRequest(bucketName, objectName);
    
        var meta = new ObjectMetadata();
        // Configure the tags in the HTTP header. 
        string str = UrlEncodeKey("key1") + "=" + UrlEncodeKey("key1") + "&" + UrlEncodeKey("key2") + "=" + UrlEncodeKey("key2");
        meta.AddHeader("x-oss-tagging", str);
        request.Metadata = meta;
    
        var result = client.InitiateMultipartUpload(request);
        uploadId = result.UploadId;
        // Display the upload ID. 
        Console.WriteLine("Init multi part upload succeeded");
        Console.WriteLine("Upload Id:{0}", result.UploadId);
    }
    catch (Exception ex)
    {
        Console.WriteLine("Init multi part upload failed, {0}", ex.Message);
    }
    // Calculate the total number of parts. 
    var partSize = 100 * 1024;
    var fi = new FileInfo(localFilename);
    var fileSize = fi.Length;
    var partCount = fileSize / partSize;
    if (fileSize % partSize != 0)
    {
        partCount++;
    }
    // Start the multipart upload task. partETags is a list of part ETags. OSS verifies each part after it receives the list of parts. After all parts are verified, OSS combines these parts into a complete object. 
    var partETags = new List<PartETag>();
    try
    {
        using (var fs = File.Open(localFilename, FileMode.Open))
        {
            for (var i = 0; i < partCount; i++)
            {
                var skipBytes = (long)partSize * i;
                // Find the start position of the multipart upload task. 
                fs.Seek(skipBytes, 0);
                // Calculate the part size in the current multipart upload task. The size of the last part is the size of the remainder after the object is split by the calculated part size. 
                var size = (partSize < fileSize - skipBytes) ? partSize : (fileSize - skipBytes);
                var request = new UploadPartRequest(bucketName, objectName, uploadId)
                {
                    InputStream = fs,
                    PartSize = size,
                    PartNumber = i + 1
                };
                // Call UploadPart to upload the parts. The returned results contain the ETag values of the parts. 
                var result = client.UploadPart(request);
                partETags.Add(result.PartETag);
                Console.WriteLine("finish {0}/{1}", partETags.Count, partCount);
            }
            Console.WriteLine("Put multi part upload succeeded");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("Put multi part upload failed, {0}", ex.Message);
    }
    // Complete the multipart upload task. 
    try
    {
        var completeMultipartUploadRequest = new CompleteMultipartUploadRequest(bucketName, objectName, uploadId);
        foreach (var partETag in partETags)
        {
            completeMultipartUploadRequest.PartETags.Add(partETag);
        }
        var result = client.CompleteMultipartUpload(completeMultipartUploadRequest);
        Console.WriteLine("complete multi part succeeded");
    }
    catch (Exception ex)
    {
        Console.WriteLine("complete multi part failed, {0}", ex.Message);
    }
  • Add tags to an object when you upload the object by using append upload

    The following code provides an example on how to add tags to an object when you upload it by using append upload:

    using Aliyun.OSS;
    using System.Text;
    // Set yourEndpoint to the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. 
    var endpoint = "yourEndpoint";
    // The AccessKey pair of an Alibaba Cloud account has permissions on all API operations. Using these credentials to access OSS is a high-risk operation. We recommend that you use a RAM user to call API operations or perform routine O&M. To create a RAM user, log on to the RAM console. 
    var accessKeyId = "yourAccessKeyId";
    var accessKeySecret = "yourAccessKeySecret";
    // Specify the bucket name. 
    var bucketName = "examplebucket";
    // Specify the full path of the object. The full path of the object cannot contain the bucket name. 
    var objectName = "exampleobject.txt";
    // Specify the full path of the local file. Example: D:\\localpath\\examplefile.txt. By default, if you do not specify the local file, the local file is uploaded from the path of the project to which the sample program belongs. 
    var localFilename = "D:\\localpath\\examplefile.txt";
    
    String UrlEncodeKey(String key)
    {
    const string CharsetName = "utf-8";
    const char separator = '/';
    var segments = key.Split(separator);
    
    var encodedKey = new StringBuilder();
    encodedKey.Append(HttpUtils.EncodeUri(segments[0], CharsetName));
    for (var i = 1; i < segments.Length; i++)
        encodedKey.Append(separator).Append(HttpUtils.EncodeUri(segments[i], CharsetName));
    
        if (key.EndsWith(separator.ToString()))
        {
            // String#split ignores trailing empty strings, e.g., "a/b/" will be split as a 2-entries array,
            // so we have to append all the trailing slash to the uri.
            foreach (var ch in key)
            {
                if (ch == separator)
                    encodedKey.Append(separator);
                else
                    break;
            }
        }
    
    return encodedKey.ToString();
    }
    // Create an OSSClient instance. 
    var client = new OssClient(endpoint, accessKeyId, accessKeySecret);
    // If the object is appended for the first time, the position from which the append operation starts is 0. The start position for the next append operation is included in the response. The position from which the next append upload starts is the current length of the object. 
    long position = 0;
    try
    {
        var metadata = client.GetObjectMetadata(bucketName, objectName);
        position = metadata.ContentLength;
    }
    catch (Exception) { }
    try
    {
        using (var fs = File.Open(localFilename, FileMode.Open))
        {
            var request = new AppendObjectRequest(bucketName, objectName)
            {
                ObjectMetadata = new ObjectMetadata(),
                Content = fs,
                Position = position
            };
    
            var meta = new ObjectMetadata();
            // Configure the tags in the HTTP header. 
             string str = UrlEncodeKey("key1") + "=" + UrlEncodeKey("key1") + "&" + UrlEncodeKey("key2") + "=" + UrlEncodeKey("key2");
            meta.AddHeader("x-oss-tagging", str);
            request.Metadata = meta;
    
            // Perform the first append operation. Only the tags configured when the object is appended for the first time are added to the object. 
            var result = client.AppendObject(request);
            // Specify the position from which the append operation starts. 
            position = result.NextAppendPosition;
            Console.WriteLine("Append object succeeded, next append position:{0}", position);
        }
        // Obtain the position for the next append operation and perform the second append operation. 
        using (var fs = File.Open(localFilename, FileMode.Open))
        {
            var request = new AppendObjectRequest(bucketName, objectName)
            {
                ObjectMetadata = new ObjectMetadata(),
                Content = fs,
                Position = position
            };
            var result = client.AppendObject(request);
            position = result.NextAppendPosition;
            Console.WriteLine("Append object succeeded, next append position:{0}", position);
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine("Append object failed, {0}", ex.Message);
    }
  • Add tags to an object when you upload the object by using resumable upload

    The following code provides an example on how to add tags to an object when you upload it by using resumable upload:

    using Aliyun.OSS;
    using Aliyun.OSS.Common;
    using System.Text;
    // Set yourEndpoint to the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. 
    var endpoint = "yourEndpoint";
    // The AccessKey pair of an Alibaba Cloud account has permissions on all API operations. Using these credentials to access OSS is a high-risk operation. We recommend that you use a RAM user to call API operations or perform routine O&M. To create a RAM user, log on to the RAM console. 
    var accessKeyId = "yourAccessKeyId";
    var accessKeySecret = "yourAccessKeySecret";
    // Specify the bucket name. 
    var bucketName = "examplebucket";
    // Specify the full path of the object. The full path of the object cannot contain the bucket name. 
    var objectName = "exampleobject.txt";
    // Specify the full path of the local file. Example: D:\\localpath\\examplefile.txt. By default, if you do not specify the local file, the local file is uploaded from the path of the project to which the sample program belongs. 
    var localFilename = "D:\\localpath\\examplefile.txt";
    // Specify the checkpoint file that records the upload result of each part. The file stores the progress information generated in the upload process. 
    string checkpointDir = "yourCheckpointDir";
    String UrlEncodeKey(String key)
    {
    const string CharsetName = "utf-8";
    const char separator = '/';
    var segments = key.Split(separator);
    
    var encodedKey = new StringBuilder();
    encodedKey.Append(HttpUtils.EncodeUri(segments[0], CharsetName));
    for (var i = 1; i < segments.Length; i++)
        encodedKey.Append(separator).Append(HttpUtils.EncodeUri(segments[i], CharsetName));
    
        if (key.EndsWith(separator.ToString()))
        {
            // String#split ignores trailing empty strings, e.g., "a/b/" will be split as a 2-entries array,
            // so we have to append all the trailing slash to the uri.
            foreach (var ch in key)
            {
                if (ch == separator)
                    encodedKey.Append(separator);
                else
                    break;
            }
        }
    
    return encodedKey.ToString();
    }
    // Create an OSSClient instance. 
    var client = new OssClient(endpoint, accessKeyId, accessKeySecret);
    try
    {
        // Configure parameters by using UploadFileRequest. 
        UploadObjectRequest request = new UploadObjectRequest(bucketName, objectName, localFilename)
        {
            // Specify the size of each part to upload. 
            PartSize = 8 * 1024 * 1024,
            // Specify the number of concurrent threads. 
            ParallelThreadCount = 3,
            // The checkpoint file is used to record the result of multipart upload. This file stores information about upload progress. If a part fails to be uploaded, the task can be continued based on the progress recorded in the checkpoint file. If you set checkpointDir to null, resumable upload does not take effect and objects are uploaded again when they fail to be uploaded. 
            CheckpointDir = checkpointDir,
        };
    
        var meta = new ObjectMetadata();
        // Configure the tags in the HTTP header. 
        string str = UrlEncodeKey("key1") + "=" + UrlEncodeKey("key1") + "&" + UrlEncodeKey("key2") + "=" + UrlEncodeKey("key2");
        meta.AddHeader("x-oss-tagging", str);
        request.Metadata = meta;
    
        // Start resumable upload. 
        client.ResumableUploadObject(request);
        Console.WriteLine("Resumable upload object:{0} succeeded", objectName);
    }
    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);
    }

Add tags to an uploaded object or modify the tags of the object

The following code provides an example on how to add tags to an uploaded object or modify the tags of the object:

using Aliyun.OSS;
// Set yourEndpoint to the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. 
var endpoint = "yourEndpoint";
// The AccessKey pair of an Alibaba Cloud account has permissions on all API operations. Using these credentials to access OSS is a high-risk operation. We recommend that you use a RAM user to call API operations or perform routine O&M. To create a RAM user, log on to the RAM console. 
var accessKeyId = "yourAccessKeyId";
var accessKeySecret = "yourAccessKeySecret";
// Specify the bucket name. 
var bucketName = "examplebucket";
// Specify the full path of the object. The full path of the object cannot contain the bucket name. 
var objectName = "exampleobject.txt";

// Create an OSSClient instance. 
var client = new OssClient(endpoint, accessKeyId, accessKeySecret);
try
{
    // Configure the tags that you want to add to the object. 
    var setRequest = new SetObjectTaggingRequest(bucketName, objectName);

    var tag1 = new Tag
    {
        Key = "project",
        Value = "projectone"
    };

    var tag2 = new Tag
    {
        Key = "user",
        Value = "jsmith"
    };

    setRequest.AddTag(tag1);
    setRequest.AddTag(tag2);
    client.SetObjectTagging(setRequest);
    Console.WriteLine("set object tagging succeeded");
}
catch (Exception ex)
{
    Console.WriteLine("set object tagging failed. {0}", ex.Message);
}

Add tags to an object when you copy the object

You can configure one of the following tagging parameters when you copy an object. Default value: Copy. Valid values:
  • Copy: The tag of the source object is copied to the destination object.
  • Replace: The tag of the destination object is set to the tag specified in the request instead of the tag of the source object.

The following examples describe how to add tags to objects smaller than 1 GB in simple copy mode and larger than 1 GB in multipart copy mode:

  • The following code provides an example on how to add tags to an object smaller than 1 GB in size when you copy the object by using simple copy:
    using Aliyun.OSS;
    using Aliyun.OSS.Common;
    using System.Text;
    
    // The AccessKey pair of an Alibaba Cloud account has permissions on all API operations. Using these credentials to access OSS is a high-risk operation. We recommend that you use a RAM user to call API operations or perform routine O&M. To create a RAM user, log on to the RAM console. 
    var accessKeyId = "yourAccessKeyId";
    var accessKeySecret = "yourAccessKeySecret";
    // Specify the name of the source bucket. Example: srcexamplebucket. 
    var sourceBucket = "srcexamplebucket";
    // Specify the full path of the source object. The full path cannot contain the bucket name. Example: srcdir/scrobject.txt. 
    var sourceObject = "srcdir/scrobject.txt";
    // Specify the name of the destination bucket. The destination bucket must be located in the same region as the source bucket. Example: destbucket. 
    var targetBucket = "destbucket";
    // Specify the full path of the destination object. The full path cannot contain the bucket name. Example: destdir/destobject.txt. 
    var targetObject = "destdir/destobject.txt";
    
    String UrlEncodeKey(String key)
    {
    const string CharsetName = "utf-8";
    const char separator = '/';
    var segments = key.Split(separator);
    
    var encodedKey = new StringBuilder();
    encodedKey.Append(HttpUtils.EncodeUri(segments[0], CharsetName));
    for (var i = 1; i < segments.Length; i++)
        encodedKey.Append(separator).Append(HttpUtils.EncodeUri(segments[i], CharsetName));
    
        if (key.EndsWith(separator.ToString()))
        {
            // String#split ignores trailing empty strings, e.g., "a/b/" will be split as a 2-entries array,
            // so we have to append all the trailing slash to the uri.
            foreach (var ch in key)
            {
                if (ch == separator)
                    encodedKey.Append(separator);
                else
                    break;
            }
        }
    
    return encodedKey.ToString();
    }
    // Create an OSSClient instance. 
    var client = new OssClient(endpoint, accessKeyId, accessKeySecret);
    try
    {
        var metadata = new ObjectMetadata();
        metadata.AddHeader("mk1", "mv1");
        metadata.AddHeader("mk2", "mv2");
    
        // Configure the tags in the HTTP header. 
        string str = UrlEncodeKey("key1") + "=" + UrlEncodeKey("key1") + "&" + UrlEncodeKey("key2") + "=" + UrlEncodeKey("key2");
        metadata.AddHeader("x-oss-tagging", str);
    
        var req = new CopyObjectRequest(sourceBucket, sourceObject, targetBucket, targetObject)
        {
            // If the value of NewObjectMetadata is null, the Copy mode is used to copy the metadata of the source object. Otherwise, the Replace mode is used to overwrite the metadata of the source object. 
            NewObjectMetadata = metadata 
        };
        // Copy the object. 
        client.CopyObject(req);
        Console.WriteLine("Copy object succeeded");
    }
    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);
    }
  • The following code provides an example on how to add tags to an object larger than 1 GB in size when you copy the object by using multipart copy:
    using Aliyun.OSS;
    using System.Text;
    
    // The AccessKey pair of an Alibaba Cloud account has permissions on all API operations. Using these credentials to access OSS is a high-risk operation. We recommend that you use a RAM user to call API operations or perform routine O&M. To create a RAM user, log on to the RAM console. 
    var accessKeyId = "yourAccessKeyId";
    var accessKeySecret = "yourAccessKeySecret";
    // Specify the name of the source bucket. Example: srcexamplebucket. 
    var sourceBucket = "srcexamplebucket";
    // Specify the full path of the source object. The full path cannot contain the bucket name. Example: srcdir/scrobject.txt. 
    var sourceObject = "srcdir/scrobject.txt";
    // Specify the name of the destination bucket. The destination bucket must be located in the same region as the source bucket. Example: destbucket. 
    var targetBucket = "destbucket";
    // Specify the full path of the destination object. The full path cannot contain the bucket name. Example: destdir/destobject.txt. 
    var targetObject = "destdir/destobject.txt";
    var uploadId = "";
    var partSize = 50 * 1024 * 1024;
    String UrlEncodeKey(String key)
    {
    const string CharsetName = "utf-8";
    const char separator = '/';
    var segments = key.Split(separator);
    
    var encodedKey = new StringBuilder();
    encodedKey.Append(HttpUtils.EncodeUri(segments[0], CharsetName));
    for (var i = 1; i < segments.Length; i++)
        encodedKey.Append(separator).Append(HttpUtils.EncodeUri(segments[i], CharsetName));
    
        if (key.EndsWith(separator.ToString()))
        {
            // String#split ignores trailing empty strings, e.g., "a/b/" will be split as a 2-entries array,
            // so we have to append all the trailing slash to the uri.
            foreach (var ch in key)
            {
                if (ch == separator)
                    encodedKey.Append(separator);
                else
                    break;
            }
        }
    
    return encodedKey.ToString();
    }
    // Create an OSSClient instance. 
    var client = new OssClient(endpoint, accessKeyId, accessKeySecret);
    try
    {
        // Initiate 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 meta = new ObjectMetadata();
        // Configure the tags in the HTTP header. 
        string str = UrlEncodeKey("key1") + "=" + UrlEncodeKey("key1") + "&" + UrlEncodeKey("key2") + "=" + UrlEncodeKey("key2");
        meta.AddHeader("x-oss-tagging", str);
        request.Metadata = meta;
    
        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 metadata = client.GetObjectMetadata(sourceBucket, sourceObject);
        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 an UploadPartCopyRequest object. You can use UploadPartCopyRequest 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
                };
            // Call the uploadPartCopy operation 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 each part after it receives the list of parts. After all 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");
    }
    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);
    }

Add tags to a symbolic link

The following code provides an example on how to add tags to a symbolic link:

using Aliyun.OSS;
using System.Text;
// Set yourEndpoint to the endpoint of the region in which the bucket is located. For example, if the bucket is located in the China (Hangzhou) region, set the endpoint to https://oss-cn-hangzhou.aliyuncs.com. 
var endpoint = "yourEndpoint";
// The AccessKey pair of an Alibaba Cloud account has permissions on all API operations. Using these credentials to access OSS is a high-risk operation. We recommend that you use a RAM user to call API operations or perform routine O&M. To create a RAM user, log on to the RAM console. 
var accessKeyId = "yourAccessKeyId";
var accessKeySecret = "yourAccessKeySecret";
// Specify the bucket name. 
var bucketName = "examplebucket";
// Specify the full path of the object. The full path of the object cannot contain the bucket name. 
var targetObjectName = "exampleobject.txt";
// Specify the name of the symbolic link. Example: symlink.txt. 
var symlinkObjectName = "symlink.txt";
var objectContent = "More than just cloud.";
String UrlEncodeKey(String key)
{
const string CharsetName = "utf-8";
const char separator = '/';
var segments = key.Split(separator);

var encodedKey = new StringBuilder();
encodedKey.Append(HttpUtils.EncodeUri(segments[0], CharsetName));
for (var i = 1; i < segments.Length; i++)
    encodedKey.Append(separator).Append(HttpUtils.EncodeUri(segments[i], CharsetName));

    if (key.EndsWith(separator.ToString()))
    {
        // String#split ignores trailing empty strings, e.g., "a/b/" will be split as a 2-entries array,
        // so we have to append all the trailing slash to the uri.
        foreach (var ch in key)
        {
            if (ch == separator)
                encodedKey.Append(separator);
            else
                break;
        }
    }

return encodedKey.ToString();
}
// Create an OSSClient instance. 
var client = new OssClient(endpoint, accessKeyId, accessKeySecret);
try
{
    // Upload the object to which the symbolic link points. 
    byte[] binaryData = Encoding.ASCII.GetBytes(objectContent);
    MemoryStream requestContent = new MemoryStream(binaryData);
    client.PutObject(bucketName, targetObjectName, requestContent);

    var meta = new ObjectMetadata();
    // Configure the tags in the HTTP header. 
    string str = UrlEncodeKey("key1") + "=" + UrlEncodeKey("key1") + "&" + UrlEncodeKey("key2") + "=" + UrlEncodeKey("key2");
    meta.AddHeader("x-oss-tagging", str);
    var request = new CreateSymlinkRequest(bucketName, symlinkObjectName, targetObjectName);
    request.ObjectMetadata = meta;
    // Create the symbolic link. 
    client.CreateSymlink(request);
    // Query the name of the object to which the symbolic link points. 
    var ossSymlink = client.GetSymlink(bucketName, symlinkObjectName);
    Console.WriteLine("Target object is {0}", ossSymlink.Target);
}
catch (Exception ex)
{
    Console.WriteLine("Failed with error info: {0}", ex.Message);
}

References

For more information about the API operation that you can call to configure object tagging, see PutObjectTagging.