更新时间:2025-09-09 GMT+08:00

JavaScript

分段上传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认证
    var token = "";
    var projectId = "";
    const region = "cn-north-4";

    var urlList;

    function initUrl(projectId) {
      let url = {
        // 创建媒资接口
        createAssetUrl: "https://vod." + region + ".myhuaweicloud.com/v1.0/" + projectId + "/asset",
        // 获取授权接口
        initAuthUrl: "https://vod." + region + ".myhuaweicloud.com/v1.0/" + projectId + "/asset/authority",
        // 确认媒资上传接口
        confirmUploadedUrl: "https://vod." + region + ".myhuaweicloud.com/v1.0/" + projectId + "/asset/status/uploaded"
      }
      return url;
    }


    // 文件分段大小1Mb,根据用户实际情况自行调整
    const bufferSize = 1024 * 1024;

    // 开始上传
    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];

      // 此处仅以MP4文件示例,其他格式可参考官网说明
      let fileType = "MP4";
      let fileContentType = "video/mp4";

      // 1. 以token鉴权为例,设置请求头
      let headers = {
        "X-Auth-Token": token,
        "Content-Type": "application/json"
      }

      // 2.创建点播媒资
      let assetRsp = createAsset(file.name, file.name, fileType, headers);

      // 3.获取初始化上传任务授权
      let initAuthResponse = getInitAuth(assetRsp, fileContentType, headers);

      // 4.初始化分段上传任务
      let uploadId = initPartUpload(initAuthResponse, assetRsp, fileContentType);

      // 文件分段计数
      let partNumber = 1;
      // 文件读取位置
      let position = 0;

      let blobSlice = File.prototype.mozSlice || File.prototype.webkitSlice || File.prototype.slice;
      // 7.循环5-6上传分段
      readAndUpload(file, blobSlice, position, partNumber, assetRsp, fileContentType, uploadId, headers,
        function (assetRsp, uploadId, headers) {
          // 8.获取列举已上传段授权
          let listAuthResp = listUploadedPartAuthority(assetRsp, uploadId, headers);
          // 9.列举已上传段
          let partInfo = listUploadedPart(listAuthResp.sign_str, assetRsp, uploadId);
          // 10.获取合并段授权
          let mergeAuthResp = mergeUploadedPartAuthority(assetRsp, uploadId, headers);
          // 11.合并段
          mergeUploadedPart(mergeAuthResp.sign_str, partInfo, assetRsp, uploadId);
          // 12.确认上传
          confirmUploaded(assetRsp.asset_id, headers);
          alert("上传结束;assetId:" + assetRsp.asset_id);
        }
      );
    }

    // 2. 创建点播媒资
    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.获取上传初始化任务授权
    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.初始化分段上传, 返回uploadId
    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.获取分段上传授权
    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),// 有特殊符号,此处加转义
        "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.上传分段
    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.循环5-6上传分段
    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);
        // 先计算文件分段md5
        const md5 = spark.end()
        // base64 encode md5字节数组
        const bytes = new Uint8Array(md5.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
        const base64 = btoa(String.fromCharCode.apply(null, bytes));
        // 5.获取分段上传授权
        let uploadAuth = getPartUploadAuthority(assetRsp, fileContentType, uploadId, content_md5, partNum, headers);
        // 6.上传分段
        uploadPartFile(uploadAuth, assetRsp, content_md5, partNum, uploadId, e.target.result);

        partNum++;

        // 判断是否读完,未读完继续上传
        if (position + bufferSize < file.size) {
          readAndUpload(file, blobSlice, position + bufferSize, partNum, assetRsp, fileContentType, uploadId, headers, afterUpload);
        } else {
          // 全部上传完成后执行后续步骤
          afterUpload(assetRsp, uploadId, headers);
        }
      }
      fileReader.readAsArrayBuffer(blobSlice.call(file, position, position + bufferSize));
    }

    // 8.获取列举分段上传授权
    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.查询已上传的分段
    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");

        // 构建合并段的参数
        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.获取合并段授权
    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.合并段
    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.确认上传
    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);
    }

    // 发送请求并返回结果,可以实际框架情况进行替换
    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()">上传</button>
</body>

</html>