全部產品
Search
文件中心

ApsaraVideo VOD:使用JavaScript SDK上傳檔案

更新時間:Jul 10, 2026

本文介紹如何使用JavaScript SDK將媒體檔案從本地上傳至點播儲存。

瀏覽器要求

瀏覽器

版本

Chrome

60+

Microsoft Edge

79+(Chromium核心)

Firefox

55+

Safari

11+

Android系統預設瀏覽器

60+

iOS系統預設瀏覽器

11+

Demo下載地址

快速整合

安裝

npm install aliyun-vod-upload-sdk

SDK 提供 ESM、CJS、UMD 三種構建產物,支援所有現代打包工具(Vite / webpack / Rollup / esbuild)。

CDN 引入

<script src="https://g.alicdn.com/apsara-media-box/imp-web-vod-upload/2.0.0/vod-upload.umd.js"></script>
<script>
  const { createUploader, UploadError } = window.VodUpload;
</script>

5 行代碼上傳

import { createUploader } from 'aliyun-vod-upload-sdk';

const uploader = createUploader({
  getAuth: () => fetch('/api/vod/auth').then(r => r.json()),
});

const { videoId } = await uploader.upload(file);

後端介面要求

SDK 不直接調用阿里雲 OpenAPI,而是通過 getAuth 回調委託你的後端擷取上傳憑證。後端需實現以下介面(對應 VOD OpenAPI):

後端介面

對應 OpenAPI

返回欄位

建立視頻上傳憑證

CreateUploadVideo

{ UploadAuth, UploadAddress, VideoId }

重新整理視頻上傳憑證

RefreshUploadVideo

{ UploadAuth, UploadAddress, VideoId }

建立圖片上傳憑證

CreateUploadImage

{ UploadAuth, UploadAddress, ImageId }

後端返回的 JSON 原樣透傳給 SDK 即可,SDK 內部自動完成 base64 解碼和欄位對應。

基礎功能

視頻上傳

import { createUploader } from 'aliyun-vod-upload-sdk';

const uploader = createUploader({
  getAuth: async (ctx) => {
    switch (ctx.kind) {
      case 'create-video':
        return fetch('/api/vod/create-auth', {
          method: 'POST',
          body: JSON.stringify({ fileName: ctx.file.name, ...ctx.meta }),
        }).then(r => r.json());
      case 'refresh-video':
        return fetch(`/api/vod/refresh-auth?videoId=${ctx.videoId}`)
          .then(r => r.json());
    }
  },
});

const result = await uploader.upload(file, {
  meta: { title: '我的視訊', cateId: 1000, tags: '教程,樣本' },
});
console.log('上傳成功,videoId:', result.videoId);

getAuth 回調接收一個 ctx 參數,SDK 會根據情境自動決定 ctx.kind

  • create-video — 首次上傳,需後端調用 CreateUploadVideo。

  • refresh-video — 續傳或憑證到期時重新整理,需後端調用 RefreshUploadVideo。

  • create-image — 上傳圖片,需後端調用 CreateUploadImage。

圖片上傳

圖片上傳使用同一個 upload() 方法,SDK 根據 file.type 自動識別(image/* 類型走圖片鏈路):

const uploader = createUploader({
  getAuth: async (ctx) => {
    if (ctx.kind === 'create-image') {
      return fetch('/api/vod/image-auth', {
        method: 'POST',
        body: JSON.stringify({ imageType: 'default' }),
      }).then(r => r.json());
    }
  },
});

const result = await uploader.upload(imageFile);
console.log('圖片上傳成功,imageId:', result.imageId);

上傳進度

const result = await uploader.upload(file, {
  onProgress: (percent, loaded, total) => {
    // percent: 0..1 (如 0.5 表示 50%)
    // loaded:  已上傳位元組數
    // total:   檔案總位元組數
    progressBar.value = percent;
    console.log(`${(percent * 100).toFixed(1)}% - ${loaded} / ${total}`);
  },
});

SDK 保證上傳完成時 percent 最終一定等於 1

取消上傳

  • 方式一:task.abort() — 最簡單

    const task = uploader.upload(file);
    
    cancelBtn.onclick = () => task.abort();
    
    try {
      const result = await task;
    } catch (e) {
      if (e.name === 'AbortError') {
        console.log('使用者取消了上傳');
      }
    }
  • 方式二:AbortSignal — 適合 React/Vue 組件生命週期

    const ctrl = new AbortController();
    cancelBtn.onclick = () => ctrl.abort();
    
    try {
      const result = await uploader.upload(file, { signal: ctrl.signal });
    } catch (e) {
      if (e.name === 'AbortError') {
        console.log('已取消');
      }
    }

取消後 checkpoint 會保留,下次再 upload(file) 會自動從斷點續傳。

斷點續傳

斷點續傳是預設開啟的,無需任何額外配置(僅視頻上傳支援,圖片上傳不走斷點續傳)。SDK 自動:

  1. 將已完成的分區進度存入 localStorage

  2. 頁面重新整理後再次 upload(file),自動從上次中斷處繼續。

  3. 匹配規則:相同 file.name + file.size + file.lastModified → 命中 checkpoint。

// 第一次上傳(上傳到 40% 時使用者關閉頁面)
await uploader.upload(file);

// 使用者重新開啟頁面後再次上傳同一個檔案
// SDK 自動從 40% 繼續,不會重複上傳
const result = await uploader.upload(file);

關閉斷點續傳:

const uploader = createUploader({
  getAuth: myGetAuth,
  checkpoint: false,  // 關閉
});

進階功能

批量上傳

SDK 提供單檔案 upload() API,批量上傳使用標準 JavaScript 非同步原語組合:

// 串列上傳(最簡單)
for (const file of files) {
  const result = await uploader.upload(file, {
    onProgress: p => updateProgress(file.name, p),
  });
  console.log(`${file.name} 完成,videoId: ${result.videoId}`);
}
// 並發上傳(大頻寬 + 小檔案)
const results = await Promise.all(
  files.map(f => uploader.upload(f)),
);
// 限並發(推薦,大檔案情境)
import pLimit from 'p-limit';

const limit = pLimit(2); // 最多同時上傳 2 個檔案
const results = await Promise.all(
  files.map(f => limit(() => uploader.upload(f, {
    onProgress: p => updateItemProgress(f, p),
  }))),
);

暫停與恢複

SDK 沒有顯式的暫停API。暫停效果通過 取消 + 續傳 實現:

// 暫停:取消當前上傳
task.abort();

// 恢複:重新上傳同一檔案,自動從 checkpoint 續傳
const result = await uploader.upload(file);

逾時取消與多源取消

// 60 秒逾時自動取消
await uploader.upload(file, {
  signal: AbortSignal.timeout(60_000),
});

// 多源取消:使用者手動取消 或 60 秒逾時
const userCtrl = new AbortController();
await uploader.upload(file, {
  signal: AbortSignal.any([
    userCtrl.signal,
    AbortSignal.timeout(60_000),
  ]),
});

React / Vue 整合

React Hook樣本

import { useEffect, useState, useRef } from 'react';
import { createUploader, UploadError } from 'aliyun-vod-upload-sdk';

function useUploader(getAuth) {
  const uploaderRef = useRef(createUploader({ getAuth }));

  useEffect(() => {
    return () => uploaderRef.current.dispose(); // 組件卸載時釋放
  }, []);

  return uploaderRef.current;
}

function UploadButton({ file }) {
  const uploader = useUploader(myGetAuth);
  const [progress, setProgress] = useState(0);

  const handleUpload = () => {
    const ctrl = new AbortController();

    uploader.upload(file, {
      signal: ctrl.signal,
      onProgress: p => setProgress(p),
    }).then(result => {
      console.log('成功', result.videoId);
    }).catch(e => {
      if (e.name !== 'AbortError') {
        console.error('失敗', e);
      }
    });
  };

  return <button onClick={handleUpload}>上傳 ({(progress * 100).toFixed(0)}%)</button>;
}

Vue 3 Composable樣本

import { onUnmounted, ref } from 'vue';
import { createUploader } from 'aliyun-vod-upload-sdk';

export function useUploader(getAuth) {
  const uploader = createUploader({ getAuth });
  const progress = ref(0);

  onUnmounted(() => uploader.dispose());

  async function upload(file) {
    return uploader.upload(file, {
      onProgress: p => { progress.value = p; },
    });
  }

  return { upload, progress };
}

API文檔

createUploader(config)

建立 Uploader 執行個體。

import { createUploader } from 'aliyun-vod-upload-sdk';

const uploader = createUploader(config);

UploaderConfig

參數

類型

必填

預設值

說明

getAuth

GetAuth

憑證擷取回調,詳見憑證回調 getAuth

retry

RetryPolicy

{ count: 3 }

OSS 分區上傳失敗時的自動重試策略

checkpoint

false | { store?: CheckpointStore }

localStorage

斷點續傳配置。false 關閉;{ store } 注入自訂 store

parallel

number

4

OSS 分區並發數

partSize

number

1048576(1MB)

OSS 分區大小(位元組)

timeout

number

60000

網路請求逾時(毫秒)

cname

string

OSS 自訂網域名

refreshSTSTokenInterval

number

300000(5min)

STS 憑證重新整理檢查間隔(毫秒)

RetryPolicy

欄位

類型

預設值

說明

count

number

3

最大重試次數

uploader.upload(file, options?)

上傳單個檔案。返回 UploadTask:既是 Promise<UploadResult>,也帶 .abort() 方法。

const task = uploader.upload(file, options);

UploadOptions

參數

類型

說明

meta

VodVideoMeta | VodImageMeta

媒資中繼資料(標題、分類、標籤等)。

signal

AbortSignal

Web 標準取消訊號。

onProgress

(percent, loaded, total) => void

進度回調,percent 範圍 0..1。

partSize

number

覆蓋全域 partSize。

parallel

number

覆蓋全域 parallel。

VodVideoMeta(視頻中繼資料)

欄位

類型

說明

title

string

視頻標題

description

string

視頻描述

cateId

number

分類別識別碼

tags

string

標籤(逗號分隔)

templateGroupId

string

轉碼模板組 ID

storageLocation

string

儲存地址

coverUrl

string

封面 URL

workflowId

string

工作流程 ID

appId

string

應用 ID

userData

Record<string, unknown>

使用者自訂資料

VodImageMeta(圖片中繼資料)

欄位

類型

說明

title

string

圖片標題

description

string

圖片描述

imageType

'default' | 'cover' | 'watermark'

圖片類型

imageExt

string

圖片副檔名

tags

string

標籤

cateId

number

分類別識別碼

storageLocation

string

儲存地址

UploadResult(上傳結果)

欄位

類型

說明

videoId

string?

視頻 ID(視頻上傳時返回)

imageId

string?

圖片 ID(圖片上傳時返回)

etag

string

OSS ETag

requestId

string

OSS 請求 ID

durationMs

number

上傳耗時(毫秒)

uploadTaskId

string

上傳任務唯一 ID(可用於售後排障)

UploadTask

upload() 返回的對象同時是 Promise<UploadResult> 和帶 .abort() 方法的對象。

type UploadTask = Promise<UploadResult> & {
  abort(): void;
};
重要

鏈式調用後 .abort() 方法會丟失。task.then(fn) 返回的是普通 Promise。如需鏈式後仍可取消,請用 options.signal

uploader.dispose()

釋放資源:取消所有進行中的上傳並清除內部狀態。

uploader.dispose();

調用後該 uploader 執行個體不可再使用。在 React/Vue 組件卸載時務必調用,避免記憶體流失。

憑證回調 getAuth

type GetAuth = (ctx: AuthContext) => Promise<AuthResult>;

AuthContext

SDK 根據上傳情境傳入不同的 ctx

ctx.kind

觸發情境

ctx 額外欄位

'create-video'

首次視頻上傳

filemeta?

'refresh-video'

續傳時重新整理憑證

filevideoId

'create-image'

圖片上傳

filemeta?

AuthResult

後端返回的 OpenAPI 原始 JSON,SDK 內部自動解碼:

欄位

類型

必填

說明

UploadAuth

string

base64 編碼的 STS 憑證

UploadAddress

string

base64 編碼的 OSS 上傳地址

VideoId

string

create-video 必填

視頻 ID

ImageId

string

create-image 必填

圖片 ID

ImageURL

string

圖片 URL(透傳)

getAuth 最簡實現

// 後端統一路由
createUploader({
  getAuth: ctx => fetch('/api/vod/auth', {
    method: 'POST',
    body: JSON.stringify(ctx),
  }).then(r => r.json()),
});

refresh-video 失敗自動降級:當續傳憑證重新整理失敗時,SDK 自動 fallback 到 create-video 重新上傳,對調用方完全透明。

錯誤處理

UploadError 結構

所有上傳錯誤都是 UploadError 執行個體(繼承自 Error):

import { UploadError } from 'aliyun-vod-upload-sdk';

class UploadError extends Error {
  readonly code: string;           // 結構化錯誤碼,如 'UPLOAD.OSS.ACCESS_DENIED'
  readonly message: string;        // 錯誤描述
  readonly suggestion: string;     // 中文修複建議
  readonly cause?: unknown;        // 底層原始錯誤
  readonly uploadTaskId?: string;  // 上傳任務 ID(可用於售後排障)
}

使用者取消上傳不是 UploadError,而是標準的 DOMExceptionname === 'AbortError')。

錯誤碼清單

錯誤碼格式:UPLOAD.{層級}.{類型}

錯誤碼

含義

修複建議

UPLOAD.AUTH.GET_AUTH_FAILED

getAuth 回調拋錯或返回結構非法

請檢查 getAuth 回調是否正確返回了包含 UploadAuth、UploadAddress 的 JSON。確認後端服務可用且返回格式正確。

UPLOAD.OSS.ACCESS_DENIED

OSS 許可權錯誤(403)

請檢查上傳憑證(STS Token)是否有效、是否已到期,以及 RAM 策略是否授權了 OSS 寫入許可權。

UPLOAD.OSS.NO_SUCH_BUCKET

Bucket 不存在

請檢查上傳地址中的 Bucket 名稱是否正確,確認 Bucket 已在對應地區建立。

UPLOAD.OSS.NO_SUCH_UPLOAD

分區上傳會話已失效

分區上傳會話已失效(可能已超過 24 小時)。SDK 將自動重新上傳,無需手動處理。

UPLOAD.OSS.UNKNOWN

OSS 未知錯誤

請查看瀏覽器控制台擷取詳細錯誤資訊,或聯絡支援人員。

UPLOAD.NETWORK.TIMEOUT

請求逾時

請開啟瀏覽器開發人員工具的 Network 面板查看失敗請求的詳情,檢查網路連接是否穩定,或嘗試增大 timeout 配置值。

UPLOAD.NETWORK.ERROR

網路連接錯誤

請開啟瀏覽器開發人員工具的 Network 面板查看失敗請求的狀態代碼和響應,確認瀏覽器可以訪問 OSS 服務地址,排查是否有代理或防火牆攔截。

UPLOAD.NETWORK.OFFLINE

瀏覽器離線

請確認網路狀態,串連網路後重試上傳。

UPLOAD.FILE.EMPTY

檔案大小為 0

檔案大小為 0,無法上傳空檔案。請檢查檔案是否正確選擇。

UPLOAD.INTERNAL.DISPOSED

Uploader 執行個體已銷毀

請重新調用 createUploader() 建立新執行個體。

使用錯誤碼常量

推薦使用 ErrorCode 常量而非字串字面量:

import { ErrorCode, UploadError } from 'aliyun-vod-upload-sdk';

try {
  await uploader.upload(file);
} catch (e) {
  if (e instanceof UploadError) {
    switch (e.code) {
      case ErrorCode.AUTH_GET_AUTH_FAILED:
        showToast('憑證擷取失敗,請重新整理頁面重試');
        break;
      case ErrorCode.NETWORK_TIMEOUT:
      case ErrorCode.NETWORK_ERROR:
        showToast('網路異常,請檢查網路後重試');
        break;
      case ErrorCode.OSS_ACCESS_DENIED:
        showToast('上傳許可權不足,請聯絡管理員');
        break;
      default:
        showToast(`上傳失敗:${e.suggestion || e.message}`);
    }
  }
}

排障指引

每個 UploadError 都攜帶 uploadTaskId,這是整次上傳的全域串聯 ID。提交給阿里雲售後時可快速定位問題:

catch (e) {
  if (e instanceof UploadError) {
    // 發給你的支援人員團隊
    const diagnostic = {
      code: e.code,
      message: e.message,
      uploadTaskId: e.uploadTaskId,
      suggestion: e.suggestion,
    };
    reportToSupport(diagnostic);
  }
}

UploadResult.uploadTaskId 同樣可用於成功上傳後的日誌關聯:

const result = await uploader.upload(file);
myLogger.info('上傳成功', { videoId: result.videoId, uploadTaskId: result.uploadTaskId });