Updated on 2025-12-01 GMT+08:00

C#

Sample code of multipart upload using C#:

using System.Net;
using System.Net.Http.Headers;
using System.Security.Cryptography;
using System.Text;
using System.Web;
using System.Xml.Linq;
using HuaweiCloud.SDK.Core;
using HuaweiCloud.SDK.Core.Auth;
using HuaweiCloud.SDK.Vod.V1;
using HuaweiCloud.SDK.Vod.V1.Model;


/**
 * Example of multipart upload
 */
public class PartUploadDemo
{
    // Set the buffer size as needed, that is, the size of the file part read each time.
    private const int BuffSize = 1024 * 1024; // 1MB

    // Region
    private const string RegionNorth4 = "cn-north-4";
    private const string RegionNorth1 = "cn-north-1";
    private const string RegionEast2 = "cn-east-2";

    // AK/SK
    private const string Ak = "";
    private const string Sk = "";

    public static void Start()
    {
        // Path of the local media asset to be uploaded
        const string filePath = "";

        // Upload the media asset.
        UploadFile(filePath);
    }

    /**
     * Multipart upload
     * @param filePath: local path where the file to be uploaded is
     */
    private static void UploadFile(string filePath)
    {
        // Verify the file and its path.
        ValidFile(filePath);

        var fileInfo = new FileInfo(filePath);
        try
        {
            // An MP4 file is used as an example. For details about other formats, see the official website.
            var fileType = "MP4";
            var fileContentType = "video/mp4";

            // 1. Initialize authentication and obtain vodClient.
            var vodClient = CreateVodClient();
            Console.WriteLine("Start to create a media asset:" + fileInfo.Name);
            // 2. Create a VOD media asset.
            var assetResponse = CreateAssetByFileUpload(vodClient, fileInfo.Name, fileInfo.Name, fileType);
            // 3. Obtain authorization for initializing an upload task.
            var initAuthResponse = InitPartUploadAuthority(vodClient, assetResponse, fileContentType);
            // 4. Initialize the upload task.
            var uploadId = InitPartUpload(initAuthResponse.SignStr, fileContentType).Result;

            // Count the number of file parts.
            var partNumber = 1;
            // Record the read length.
            var bytesRead = 0;

            // 7. Read the file content and repeat steps 5 and 6 to upload all parts.
            var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            // Create a buffer.
            var buffer = new byte[BuffSize];
            while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) > 0)
            {
                // If the read length is less than the buffer length, copy the data within the valid length and apply the data to the last part.
                if (bytesRead != BuffSize)
                {
                    var tmp = new byte[bytesRead];
                    Array.Copy(buffer, tmp, bytesRead);
                    buffer = tmp;
                }

                // Calculate an MD5 hash value.
                var hashBytes = MD5.HashData(buffer);
                // Convert the MD5 hash value to a Base64 string.
                var contentMd5 = Convert.ToBase64String(hashBytes);
                // Output the Base64 string.
                Console.WriteLine("The MD5 value of the "+(partNumber)+" part in the file is " + contentMd5);
                // 5. Obtain authorization for multipart upload.
                var partUploadAuthorityResponse =
                    GetPartUploadAuthority(vodClient, fileContentType, assetResponse, contentMd5, uploadId, partNumber);
                // 6. Upload parts.
                var flag = UploadPartFile(partUploadAuthorityResponse.SignStr, buffer, contentMd5).Result;
                // The part number automatically increments by one.
                partNumber++;
            }

            fileStream.Close();
            // 8. Obtain authorization for obtaining uploaded parts.
            var listPartUploadAuthorityResponse = ListUploadedPartAuthority(vodClient, assetResponse, uploadId);
            // 9. Obtain uploaded parts.
            var partInfo = ListUploadedPart(listPartUploadAuthorityResponse.SignStr).Result;
            // 10. Obtain authorization for merging parts.
            var mergePartUploadAuthorityResponse = MergeUploadedPartAuthority(vodClient, assetResponse, uploadId);
            // 11. Merge uploaded parts.
            _ = MergeUploadedPart(mergePartUploadAuthorityResponse.SignStr, partInfo).Result;
            // 12. Confirm the media asset upload.
            ConfirmUploaded(vodClient, assetResponse);
            Console.WriteLine("Media asset created. assetId: " + assetResponse.AssetId");

        }
        catch (RequestTimeoutException requestTimeoutException)
        {
            Console.WriteLine(requestTimeoutException.ErrorMessage);
        }
        catch (ServiceResponseException clientRequestException)
        {
            Console.WriteLine(clientRequestException.HttpStatusCode);
            Console.WriteLine(clientRequestException.RequestId);
            Console.WriteLine(clientRequestException.ErrorCode);
            Console.WriteLine(clientRequestException.ErrorMessage);
        }
        catch (ConnectionException connectionException)
        {
            Console.WriteLine(connectionException.ErrorMessage);
        }
        catch (Exception exception)
        {
            Console.WriteLine(exception.Message);
        }
    }

    /**
     * Verify the file and its path.
     * @param filePath
     */
    private static void ValidFile(string filePath)
    {
        var fileExists = File.Exists(filePath);
        if (!fileExists)
        {
            // File not found.
            throw new Exception("File not found.");
        }
    }

    /**
     * 1. Construct authentication.
     * @return
     */
    private static VodClient CreateVodClient()
    {
        var config = HttpConfig.GetDefaultConfig();
        config.IgnoreSslVerification = true;
        var auth = new BasicCredentials(Ak, Sk);

        var client = VodClient.NewBuilder()
            .WithCredential(auth)
            .WithRegion(VodRegion.ValueOf(RegionNorth4))
            .WithHttpConfig(config)
            .Build();
        return client;
    }

    /**
     * 2. Create a media asset.
     * @param client
     * @param title
     * @param videoName
     * @param video
     * @return
     */
    private static CreateAssetByFileUploadResponse CreateAssetByFileUpload(VodClient client, string title,
        string videoName,
        string videoType)
    {
        Console.WriteLine("createAssetByFileUpload start");
        var req = new CreateAssetByFileUploadRequest
        {
            Body = new CreateAssetByFileUploadReq()
            {
                Title = title,
                VideoName = videoName,
                VideoType = videoType
            }
        };
        // Call the API for creating a media asset.
        var response = client.CreateAssetByFileUpload(req);
        Console.WriteLine("createAssetByFileUpload end; createAssetResponse:" + response);
        return response;
    }

    /**
     * 3. Obtain authorization for initializing an upload task.
     * @param client
     * @param assetResponse
     * @param fileContentType
     */
    private static ShowAssetTempAuthorityResponse InitPartUploadAuthority(VodClient client,
        CreateAssetByFileUploadResponse assetResponse,
        string fileContentType)
    {
        Console.WriteLine("Obtain authorization for initializing an upload task. initPartUploadAuthority start");
        // Configure parameters.
        var request = new ShowAssetTempAuthorityRequest
        {
            HttpVerb = "POST",
            Bucket = assetResponse.Target.Bucket,
            ObjectKey = assetResponse.Target.Object,
            ContentType = fileContentType
        };
        // Send an initialization request.
        var response = client.ShowAssetTempAuthority(request);
        Console.WriteLine("Obtain authorization for initializing an upload task. initPartUploadAuthority end; response" + response);
        return response;
    }

    /**
     * 4. Initialize multipart upload.
     * @param signStr
     * @param contentType
     * @return
     */
    private static async Task<string?> InitPartUpload(string signStr, string contentType)
    {
        Console.WriteLine("Initialize multipart upload. initPartUpload start");
        using var client = new HttpClient();
        var content = new ByteArrayContent([]);
        content.Headers.ContentType = new MediaTypeHeaderValue(contentType);
        var response = await client.PostAsync(signStr, content);
        // Ensure that the request is successful.
        response.EnsureSuccessStatusCode();
        var responseBody = await response.Content.ReadAsStringAsync();
        Console.WriteLine(responseBody);
        var root = XElement.Parse(responseBody);
        var ns = root.Name.Namespace;
        var uploadId = root.Element("{" + ns + "}UploadId")?.Value;
        Console.WriteLine("Initialize multipart upload. initPartUpload end; UploadId:" + uploadId);
        return uploadId;
    }

    /**
     * 5. Obtain authorization for multipart upload.
     * @param client
     * @param fileContentType
     * @param assetResponse
     * @param contentMd5
     * @param uploadId
     * @param partNumber
     * @return
     */
    private static ShowAssetTempAuthorityResponse GetPartUploadAuthority(VodClient client,
        string fileContentType, CreateAssetByFileUploadResponse assetResponse, string contentMd5,
        string uploadId, int partNumber)
    {
        // Thread.Sleep(2000);
        Console.WriteLine ("Obtain authorization for multipart upload. GetPartUploadAuthority start:" + partNumber);
        // Configure parameters.
        var request = new ShowAssetTempAuthorityRequest
        {
            HttpVerb = "PUT",
            Bucket = assetResponse.Target.Bucket,
            ObjectKey = assetResponse.Target.Object,
            ContentType = fileContentType,
            // ContentMd5 = contentMd5,
            ContentMd5 = HttpUtility.UrlEncode(contentMd5),
            UploadId = uploadId,
            PartNumber = partNumber
        };
        Console.WriteLine(WebUtility.UrlEncode(contentMd5));
        var response = client.ShowAssetTempAuthority(request);
        Console.WriteLine ("Obtain authorization for multipart upload. GetPartUploadAuthority end; response" + partNumber + "; response: " + response + "\n");
        return response;
    }

    /**
     * 6. Upload parts.
     * @param signStr
     * @param fileByte
     * @param contentMd5
     */
    public static async Task<Boolean> UploadPartFile(string signStr, byte[] fileByte, string contentMd5)
    {
        Console.WriteLine("Upload parts. uploadPartFile start");
        HttpClient client = new HttpClient();
        HttpContent content = new ByteArrayContent(fileByte);
        content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
        content.Headers.Add("Content-MD5", contentMd5);
        HttpResponseMessage response = await client.PutAsync(signStr, content);
        Console.WriteLine("Upload parts. response:" + response);
        response.EnsureSuccessStatusCode(); // Ensure that the request is successful.
        var responseBody = await response.Content.ReadAsStringAsync();
        Console.WriteLine("Upload parts. uploadPartFile end; response:" + responseBody);
        return true;
    }

    /**
     * 8. Obtain authorization for listing uploaded parts.
     * @param client
     * @param assetResponse
     * @param uploadId
     * @return
     */
    private static ShowAssetTempAuthorityResponse ListUploadedPartAuthority(VodClient client,
        CreateAssetByFileUploadResponse assetResponse, string? uploadId)
    {
        Console.WriteLine("Obtain authorization for listing uploaded parts. listUploadedPartAuthority start");
        var request = new ShowAssetTempAuthorityRequest
        {
            HttpVerb = "GET",
            Bucket = assetResponse.Target.Bucket,
            ObjectKey = assetResponse.Target.Object,
            UploadId = uploadId
        };
        var response = client.ShowAssetTempAuthority(request);
        Console.WriteLine("Obtain authorization for listing uploaded parts. listUploadedPartAuthority end; response: " + response);
        return response;
    }

    /**
     * 9. Query uploaded parts.
     * @param signStr
     * @return
     */
    private static async Task<string> ListUploadedPart(string signStr)
    {
        Console.WriteLine("Query uploaded parts. listUploadedPart start");
        var partNumberMarker = 0;
        var mergerRoot = new XElement("CompleteMultipartUpload");
        while (true)
        {
            // List parts.
            using var client = new HttpClient();
            var response = await client.GetAsync(signStr + "&part-number-marker=" + partNumberMarker);
            response.EnsureSuccessStatusCode();
            var responseBody = await response.Content.ReadAsStringAsync();
            Console.WriteLine(responseBody);
            var root = XElement.Parse(responseBody);
            var ns = root.Name.Namespace;
            var partNodes = root.Elements("{" + ns + "}Part");
            var partNumberMarkerElement = root.Element("{" + ns + "}NextPartNumberMarker");
            foreach (var partNode in partNodes)
            {
                var partNumberNode = partNode.Element("{" + ns + "}PartNumber");
                var partNumber = partNumberNode?.Value;
                var eTagNode = partNode.Element("{" + ns + "}ETag");
                var etag = eTagNode?.Value;
                var part = new XElement("Part");
                mergerRoot.Add(part);
                part.Add(new XElement("PartNumber", partNumber));
                part.Add(new XElement("ETag", etag));
            }
            partNumberMarker = Convert.ToInt32(partNumberMarkerElement?.Value);
            if (partNumberMarker == 0 || partNumberMarker % 1000 != 0)
            {
                break;
            }
        }
        Console.WriteLine(mergerRoot.ToString());
        Console.WriteLine("Query uploaded parts. listUploadedPart end");
        return mergerRoot.ToString();
    }

    /**
     * 10. Obtain authorization for merging parts.
     * @param client
     * @param assetResponse
     * @param uploadId
     * @return
     */
    private static ShowAssetTempAuthorityResponse MergeUploadedPartAuthority(VodClient client,
        CreateAssetByFileUploadResponse assetResponse, string? uploadId)
    {
        Console.WriteLine("Obtain authorization for merging parts. mergeUploadedPartAuthority start");
        var request = new ShowAssetTempAuthorityRequest
        {
            HttpVerb = "POST",
            Bucket = assetResponse.Target.Bucket,
            ObjectKey = assetResponse.Target.Object,
            UploadId = uploadId
        };
        var response = client.ShowAssetTempAuthority(request);
        Console.WriteLine("Obtain authorization for merging parts. mergeUploadedPartAuthority end; response: " + response);
        return response;
    }

    /**
     * 11. Merge parts.
     */
    public static async Task<Boolean> MergeUploadedPart(string signStr, string partInfo)
    {
        Console.WriteLine("Merge parts. mergeUploadedPart start");
        using var client = new HttpClient();
        // Add content-Type to the request header and set the value to application/xml.
        HttpContent content = new StringContent(partInfo);
        content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
        var response = await client.PostAsync(signStr, content);
        var responseBody = await response.Content.ReadAsStringAsync();
        Console.WriteLine(responseBody);
        response.EnsureSuccessStatusCode();
        Console.WriteLine("Merge parts. mergeUploadedPart end");
        return true;
    }

    /**
     * Confirm the upload completion.
     */
    private static void ConfirmUploaded(VodClient client, CreateAssetByFileUploadResponse assetResponse)
    {
        Console.WriteLine("Confirm the upload completion. confirmUploaded start");
        var request = new ConfirmAssetUploadRequest
        {
            Body = new ConfirmAssetUploadReq()
            {
                Status = ConfirmAssetUploadReq.StatusEnum.FromValue("CREATED"),
                AssetId = assetResponse.AssetId
            }
        };
        var response = client.ConfirmAssetUpload(request);
        Console.WriteLine("Upload completed. assetId: " + response);
    }
}