Updated on 2026-01-09 GMT+08:00

JavaScript

Multipart upload can be completed using different languages.

The following is the sample code in JavaScript:

<!DOCTYPE html>
<html>

<head>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/spark-md5/3.0.2/spark-md5.min.js"></script>
  <script>
    // Token authentication
    var token = "";
    var projectId = "";
    const region = "cn-north-4";

    var urlList;

    function initUrl(projectId) {
      let url = {
        // API for creating a media asset.
        createAssetUrl: "https://vod." + region + ".myhuaweicloud.com/v1.0/" + projectId + "/asset",
        // API for obtaining authorization.
        initAuthUrl: "https://vod." + region + ".myhuaweicloud.com/v1.0/" + projectId + "/asset/authority",
        // API for confirming media asset upload.
        confirmUploadedUrl: "https://vod." + region + ".myhuaweicloud.com/v1.0/" + projectId + "/asset/status/uploaded"
      }
      return url;
    }


    // The file part size is 1 MB (adjustable).
    const bufferSize = 1024 * 1024;

    // Start the upload.
    function startUpload() {
      token = document.getElementById("token").value;
      projectId = document.getElementById("projectId").value;

      urlList = initUrl(projectId);

      let files = document.getElementById("file").files;
      let file = files[0];

      // An MP4 file is used as an example. For details about other formats, see the official website.
      let fileType = "MP4";
      let fileContentType = "video/mp4";

      // 1. Set the request header. Token authentication is used as an example.
      let headers = {
        "X-Auth-Token": token,
        "Content-Type": "application/json"
      }

      // 2. Create a VOD media asset.
      let assetRsp = createAsset(file.name, file.name, fileType, headers);

      // 3. Obtain authorization for initializing an upload task.
      let initAuthResponse = getInitAuth(assetRsp, fileContentType, headers);

      // 4. Initialize the multipart upload task.
      let uploadId = initPartUpload(initAuthResponse, assetRsp, fileContentType);

      // 5. Count the number of file parts.
      let partNumber = 1;
      // 6. Specify the location where file data is read.
      let position = 0;

      let blobSlice = File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice;
      // 7. Repeat steps 5 to 6 to upload parts.
      readAndUpload(file, blobSlice, position, partNumber, assetRsp, fileContentType, uploadId, headers,
        function (assetRsp, uploadId, headers) {
          // 8. Obtain authorization for listing uploaded parts.
          let listAuthResp = listUploadedPartAuthority(assetRsp, uploadId, headers);
          // 9. List uploaded parts.
          let partInfo = listUploadedPart(listAuthResp.sign_str, assetRsp, uploadId);
          // 10. Obtain authorization for merging parts.
          let mergeAuthResp = mergeUploadedPartAuthority(assetRsp, uploadId, headers);
          // 11. Merge parts.
          mergeUploadedPart(mergeAuthResp.sign_str, partInfo, assetRsp, uploadId);
          // 12. Confirm the upload.
          confirmUploaded(assetRsp.asset_id, headers);
          alert("Uploaded;assetId:" + assetRsp.asset_id);
        }
      );
    }

    // 2. Create a VOD media asset.
    function createAsset(title, videoName, videoType, headers) {
      let body = {
        "title": title,
        "video_name": videoName,
        "video_type": videoType
      }
      let resp = sendRequest("POST", urlList.createAssetUrl, JSON.stringify(body), headers);
      return JSON.parse(resp);
    }

    // 3. Obtain authorization for initializing an upload task.
    function getInitAuth(assetRsp, fileContentType, headers) {
      let params = {
        "http_verb": "POST",
        "content_type": fileContentType,
        "bucket": assetRsp.target.bucket,
        "object_key": assetRsp.target.object
      }
      let temp = "?";
      for (let e in params) {
        temp += e + "=" + params[e] + "&";
      }
      let resp = sendRequest("GET", urlList.initAuthUrl + temp, null, headers);
      return JSON.parse(resp);
    }

    // 4. Initialize the multipart upload task. An upload ID will be returned.
    function initPartUpload(signStr, assetRsp, contentType) {
      let initUrl = "https://" + assetRsp.target.bucket + ".obs." + region + ".myhuaweicloud.com/"
        + assetRsp.target.object + "?uploads&" + signStr.sign_str;
      let resp = sendRequest("POST", initUrl, null, { "Content-Type": contentType });
      let domParser = new DOMParser();
      let dom = domParser.parseFromString(resp, "text/xml");
      return dom.getElementsByTagName("UploadId")[0].firstChild.nodeValue;
    }

    // 5. Obtain authorization for multipart upload.
    function getPartUploadAuthority(assetRsp, fileContentType, uploadId, contentMd5, partNumber, headers) {
      let params = {
        "http_verb": "PUT",
        "content_type": fileContentType,
        "bucket": assetRsp.target.bucket,
        "object_key": assetRsp.target.object,
        "content_md5": encodeURIComponent(contentMd5),// There are special characters, which should be escaped.
        "upload_id": uploadId,
        "part_number": partNumber
      }
      let temp = "?";
      for (let e in params) {
        temp += e + "=" + params[e] + "&";
      }
      let resp = sendRequest("GET", urlList.initAuthUrl + temp, null, headers);
      return JSON.parse(resp);
    }

    // 6. Upload parts.
    function uploadPartFile(uploadAuth, assetRsp, contentMd5, partNumber, uploadId, content) {
      let url = "https://" + assetRsp.target.bucket + ".obs." + region + ".myhuaweicloud.com/"
        + assetRsp.target.object + "?partNumber=" + partNumber + "&uploadId=" + uploadId + "&" + uploadAuth.sign_str;
      let headers = {
        "Content-Type": "application/octet-stream",
        "Content-MD5": contentMd5
      }
      let resp = sendRequest("PUT", url, content, headers);
    }

    // 7. Repeat steps 5 to 6 to upload parts.
    function readAndUpload(file, blobSlice, position, partNum, assetRsp, fileContentType, uploadId, headers, afterUpload) {
      let fileReader = new FileReader();
      const spark = new SparkMD5.ArrayBuffer();
      fileReader.onload = function (e) {
        spark.append(e.target.result);
        // Calculate the MD5 value of a file part.
        const md5 = spark.end()
        // Encode the MD5 byte array using Base64.
        const bytes = new Uint8Array(md5.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
        const base64 = btoa(String.fromCharCode.apply(null, bytes));
        // 5. Obtain authorization for multipart upload.
        let uploadAuth = getPartUploadAuthority(assetRsp, fileContentType, uploadId, content_md5, partNum, headers);
        // 6. Upload parts.
        uploadPartFile(uploadAuth, assetRsp, content_md5, partNum, uploadId, e.target.result);

        partNum++;

        // Check whether the data read is complete. If not, continue the upload.
        if (position + bufferSize < file.size) {
          readAndUpload(file, blobSlice, position + bufferSize, partNum, assetRsp, fileContentType, uploadId, headers, afterUpload);
        } else {
          // After the upload is complete, perform the subsequent steps.
          afterUpload(assetRsp, uploadId, headers);
        }
      }
      fileReader.readAsArrayBuffer(blobSlice.call(file, position, position + bufferSize));
    }

    // 8. Obtain authorization for listing uploaded parts.
    function listUploadedPartAuthority(assetRsp, uploadId, headers) {
      let params = {
        "http_verb": "GET",
        "bucket": assetRsp.target.bucket,
        "object_key": assetRsp.target.object,
        "upload_id": uploadId
      }
      let temp = "?";
      for (let e in params) {
        temp += e + "=" + params[e] + "&";
      }
      let resp = sendRequest("GET", urlList.initAuthUrl + temp, null, headers);
      return JSON.parse(resp);
    }

    // 9. Query uploaded parts.
    function listUploadedPart(signStr, assetRsp, uploadId) {
      let url = "https://" + assetRsp.target.bucket + ".obs." + region + ".myhuaweicloud.com/"
        + assetRsp.target.object + "?" + signStr + "&uploadId=" + uploadId;
      let nextPartNumberMarker = 0;

      let result = "<CompleteMultipartUpload>";
      let domParser = new DOMParser();
      while (true) {
        let resp = sendRequest("GET", url + "&part-number-marker=" + nextPartNumberMarker, null, {});
        let dom = domParser.parseFromString(resp, "text/xml");
        let part = dom.getElementsByTagName("Part");

        // Construct parameters for merging parts.
        for (let i = 0; i < part.length; i++) {
          let ele = part[i];
          let num = ele.getElementsByTagName("PartNumber")[0].firstChild.nodeValue;
          let tag = ele.getElementsByTagName("ETag")[0].firstChild.nodeValue;
          result += "<Part>" +
            "<PartNumber>" + num + "</PartNumber>" +
            "<ETag>" + tag + "</ETag>" +
            "</Part>"
            ;
        }

        nextPartNumberMarker = Number(dom.getElementsByTagName("NextPartNumberMarker")[0].firstChild.nodeValue);

        if (nextPartNumberMarker % 1000 != 0) {
          break;
        }
      }
      result += "</CompleteMultipartUpload>";
      return result;
    }

    // 10. Obtain authorization for merging parts.
    function mergeUploadedPartAuthority(assetRsp, uploadId, headers) {
      let params = {
        "http_verb": "POST",
        "bucket": assetRsp.target.bucket,
        "object_key": assetRsp.target.object,
        "upload_id": uploadId
      }
      let temp = "?";
      for (let e in params) {
        temp += e + "=" + params[e] + "&";
      }
      let resp = sendRequest("GET", urlList.initAuthUrl + temp, null, headers);
      return JSON.parse(resp);
    }

    // 11. Merge parts.
    function mergeUploadedPart(signStr, partInfo, assetRsp, uploadId) {
      let url = "https://" + assetRsp.target.bucket + ".obs." + region + ".myhuaweicloud.com/"
        + assetRsp.target.object + "?" + signStr + "&uploadId=" + uploadId;
      let resp = sendRequest("POST", url, partInfo, { "Content-Type": "application/xml" });
    }

    // 12. Confirm the upload.
    function confirmUploaded(assetId, headers) {
      let body = {
        "asset_id": assetId,
        "status": "CREATED"
      };
      let resp = sendRequest("POST", urlList.confirmUploadedUrl, JSON.stringify(body), headers);
      return console.log(resp);
    }

    // Send a request and wait for result return. Change the content based on the actual framework.
    function sendRequest(method, url, data, headers) {
      var xhr = new XMLHttpRequest();
      xhr.open(method, url, false);
      for (let i in headers) {
        xhr.setRequestHeader(i, headers[i]);
      }
      xhr.send(data);
      return xhr.responseText;
    }
  </script>
</head>

<body>
  <p>projectId: <input type="input" id="projectId"></input></p>
  <p>token: <input type="input" id="token"></input></p>
  <p><input type="file" id="file"></input></p>
  <button onclick="startUpload()">Upload</button>
</body>

</html>