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。