createUploader

上傳工具工廠,與舊後端上傳介面相容。回傳 { uploadImage, uploadVideo, uploadFile, queryVideo } 四個方法,覆蓋圖片單次上傳、影片並行分片 + 轉碼輪詢、一般檔案(PDF / 文件 / zip)分片上傳三類場景。

📌 業務側優先用 useUploader composable 或 SwUploadImage / SwUploadVideo / SwUploadFile 元件 —— framework 插件已用 createUploader 建立好實例並接好 systemInfo / userId。直接呼叫 createUploader 主要出現在自訂注入場景(如非標準 store 來源)。

運作原理

  1. 工廠以 getter 形式注入系統設定與 userId(getSystemInfo / getUserId),每次呼叫即時取值,避免 stale closure。
  2. uploadImage:本機先校驗位元組數(圖片按 upload_image_max_length,未設定才回退 upload_file_max_length);單次 POST,回應的 file_info(形如 "1280x720")解析為 width / height。
  3. uploadVideo:對每個 File 獨立實例化分片上傳器,並行互不阻塞;預設分片 2MB、單片重試 3 次。
  4. uploadFile:與影片共用同一分片端點,但無轉碼二段(不呼叫 queryVideo)—— 分片完成回應即含可存取 URL,以 Promise 回傳。
  5. queryVideo:每 3 秒輪詢轉碼結果,直到 file_m3u8 可用或重試耗盡(預設 20 次 ≈ 60 秒)。

介面 URL(upload_image_url 等)來自 systemInfo、已含公共參數;上傳逾時統一走 swift.config.json request 區塊的 uploadTimeout(預設 600s)。

API

const uploader = createUploader({
  getSystemInfo: () => useSystemStore().info as SystemInfo,
  getUserId: () => useUserStore().id,
})
方法簽名說明
uploadImage(file: File, thumbnail: boolean) => Promise<{ id, value, width, height }>單次上傳圖片;thumbnail 是否壓縮為縮圖;value 為檔案 URL
uploadVideo(files: File[], options?) => void並行分片上傳影片;結果走 onProcess / onSuccess / onError 回呼
uploadFile(file: File, options?) => Promise<{ id, value, file_md5 }>分片上傳一般檔案,無轉碼;value 為檔案存取 URL
queryVideo(id: string, retries = 20) => Promise<VideoQueryResult>輪詢轉碼結果;回傳 { id, file_m3u8, file_duration, file_preview_m3u8, file_status, width, height }

uploadVideo / uploadFile 的 options:chunkSize(預設 2MB)/ retries(預設 3)/ onProcess(進度 { processed_num, chunk_total })/ onBefore;uploadVideo 另有 onSuccess / onError,uploadFile 另有 concurrency(並行分片數,預設 6)。

使用範例

// 上傳圖片並寫入表單
const { id, value, width, height } = await uploader.uploadImage(file, false)
form.cover = { id, url: value, w: width, h: height }

// 分片上傳影片 + 進度回饋 + 轉碼查詢
uploader.uploadVideo([videoFile], {
  onProcess: ({ processed_num, chunk_total }) => (progress.value = processed_num / chunk_total),
  onSuccess: async ({ id }) => {
    form.video = await uploader.queryVideo(id)
  },
  onError: (e) => console.error('upload failed', e),
})

// 一般檔案(Promise 風格,無轉碼二段)
const { value: fileUrl } = await uploader.uploadFile(pdfFile, {
  onProcess: ({ processed_num, chunk_total }) => (progress.value = processed_num / chunk_total),
})

注意事項

queryVideo 預設 ~60 秒逾時即拋錯(UUP-001,warning)

預設輪詢 20 次 × 3 秒間隔,耗盡即拋 Error('查询重试次数已用尽')。後端轉碼慢 / 故障時業務會卡 60 秒後拋錯 —— 必須 try/catch + UI 回饋,或按需加大輪次:

onSuccess: async ({ id }) => {
  try {
    form.video = await uploader.queryVideo(id)
  } catch {
    useToast().info('影片處理中,請稍後在草稿裡查看') // 或 queryVideo(id, 40)
  }
}

其他

  • 僅瀏覽器可用(依賴 File / FormData / Blob),SSR 期不要呼叫。
  • uploadImage 超出圖片體積上限時拋 Error('文件超出大小')(上限取 upload_image_max_length,未設定回退 upload_file_max_length);file_info / file_quality 尺寸解析失敗也會拋明確錯誤。
  • uploadVideo 每次失敗靜默重試該分片,直到 retries 耗盡才走 onError。