本项目基于 Vue 3 + TypeScript + Vite,目标是沉淀一个性能好、扩展性高、稳定性强的大文件上传前端基础设施,既可以以 npm 插件方式接入现有项目,也提供开箱即用的 Demo 页面便于调试与验证。
npm install jhl-large-file-upload本项目基于 Vue 3 + TypeScript + Vite,目标是沉淀一个性能好、扩展性高、稳定性强的大文件上传前端基础设施,既可以以 npm 插件方式接入现有项目,也提供开箱即用的 Demo 页面便于调试与验证。
- 面向大文件/超大文件上传场景,支持 GB 级文件稳定传输
- 提供 UI + 逻辑一体的「开箱即用模式」,也提供仅暴露能力的「业务自定义模式」
- 通过合理抽象 Props、事件和暴露方法,方便在不同业务中复用和二次封装
- 与后端通过固定协议对接,降低前后端协同时的沟通成本
- 文件分片上传:前端按固定大小切片,控制并发数上传,降低大文件长连接风险
- 断点续传:通过校验接口返回的已上传切片列表,实现中断后的续传
- 秒传支持:利用文件 Hash 判断服务端是否已存在文件,避免重复上传
- 进度可视化:单文件 Hash 进度、整体上传进度、切片网格状态一目了然
- 并发调度:内置上传调度器,控制最大并发,支持暂停/恢复
- SSE 合并结果通知:支持耗时合并任务的异步结果回传
- 插槽扩展:内置 UI 可用,也可完全隐藏 UI,自定义触发入口和交互
- 组件实例 API:通过 ref 获取内部上传列表、切片状态,进行业务侧联动
- 上传组件核心:位于 src/components/Upload.vue,负责完整上传流程的编排,包括切片、Hash 计算、校验、分片上传、合并请求、SSE 监听等
- Demo 页面:位于 src/App.vue,作为插件在真实业务中的使用示例,展示默认 UI + 插槽扩展的写法
- 构建与开发:通过 Vite 提供本地开发、打包和预览能力,可在此基础上抽离为独立 npm 包发布
1. 选择文件:通过默认按钮或插槽自定义入口触发原生
2. 文件切片:按配置的切片大小将文件切分为多个 Blob,对每个切片建立上传任务
3. Hash 计算:在 Web Worker 中对所有切片进行 Hash 计算,避免阻塞主线程,并持续回传进度
4. 上传前校验:调用后端校验接口,判断是否需要上传以及哪些切片已存在
5. 分片上传:根据校验结果,仅上传缺失的切片,并按最大并发数调度请求,实时更新进度和状态
6. 合并请求:所有切片上传完成后,向后端发送合并请求,必要时通过 SSE 监听合并结果
7. 状态管理:在上传过程中维护文件级和切片级状态,支持暂停、恢复、取消以及记录删除等操作
当前仓库中的 client 目录既包含上传组件实现,也包含一个 Demo 页面,方便在本地直接体验大文件上传流程:
- 开发调试:在 client 目录执行 npm install 后运行 npm run dev,打开浏览器访问本地地址即可
- 构建产物:执行 npm run build 生成构建结果,可用于后续封装发布或集成到其他系统
- 预览构建:执行 npm run preview 本地预览 build 后的资源
``bash`
npm i jhl-large-file-upload
全局注册
`ts
import { createApp } from "vue";
import App from "./App.vue";
import { Upload } from "jhl-large-file-upload";
import "jhl-large-file-upload/dist/style.css";
const app = createApp(App);
app.component("Upload", Upload);
app.mount("#app");
`
局部注册
`ts`
import { Upload } from "jhl-large-file-upload";
import "jhl-large-file-upload/dist/style.css";
`vue
:sse-url="sseUrl"
:headers="headers"
:chunk-size="chunkSize"
:max-concurrency="maxConcurrency"
:show-ui="true"
/>
`
`vue`
`ts
import { ref } from "vue";
import type { UploadExpose } from "@your-org/vue-large-upload";
const uploaderRef = ref
uploaderRef.value?.uploadFiles(files);
uploaderRef.value?.pauseUpload(fileId);
uploaderRef.value?.resumeUpload(fileId);
uploaderRef.value?.cancelUpload(fileId);
const fileState = uploaderRef.value?.getFileState(fileId);
const chunkState = uploaderRef.value?.getChunkState(fileId, chunkIndex);
const allFiles = uploaderRef.value?.fileList;
`
#### Props 回调(函数签名与参数说明)
- onStart(item: FileUploadItem):单文件开始进入 Hash 计算时触发onHashProgress(item: FileUploadItem, percentage: number)
- :Hash 计算进度回调(0–100)onVerified(item: FileUploadItem, shouldUpload: boolean, uploadedChunks: string[])
- :校验结果回调,包含是否需要上传以及已存在切片列表onChunkProgress(item: FileUploadItem, chunkIndex: number, percentage: number)
- :单切片上传进度回调onChunkSuccess(item: FileUploadItem, chunkIndex: number)
- :单切片上传成功回调onChunkError(item: FileUploadItem, chunkIndex: number, error: unknown)
- :单切片上传失败回调onPaused(item: FileUploadItem)
- :单文件被暂停时触发onResumed(item: FileUploadItem)
- :单文件从暂停恢复继续上传时触发onCancel(item: FileUploadItem)
- :单文件被取消上传时触发onMerge(item: FileUploadItem)
- :所有切片上传完成,正式发起合并请求时触发onComplete(item: FileUploadItem)
- :单文件整体完成(包含秒传场景)时触发onError(item: FileUploadItem, error: unknown)
- :单文件上传流程出现异常时触发
> 说明:FileUploadItem 与组件内部状态结构一致,可通过 UploadExpose.fileList 或回调参数中的 item 获取该文件的 Hash、进度、切片列表等完整信息。
#### Emits 事件(事件名与参数)
- start:(item: FileUploadItem)hash-progress
- :(item: FileUploadItem, percentage: number)verified
- :(item: FileUploadItem, shouldUpload: boolean, uploadedChunks: string[])chunk-progress
- :(item: FileUploadItem, chunkIndex: number, percentage: number)chunk-success
- :(item: FileUploadItem, chunkIndex: number)chunk-error
- :(item: FileUploadItem, chunkIndex: number, error: unknown)paused
- :(item: FileUploadItem)resumed
- :(item: FileUploadItem)cancel
- :(item: FileUploadItem)merge
- :(item: FileUploadItem)complete
- :(item: FileUploadItem)error
- :(item: FileUploadItem, error: unknown)
- uploadUrl:上传服务基础地址(baseURL)
- checkUrl:校验接口地址,默认 /check
- uploadActionUrl:上传分片接口地址,默认 /upload
- mergeUrl:合并接口地址,默认 /merge
- cancelUrl:取消接口地址,默认 /cancel
- sseUrl:SSE 地址,用于监听合并完成
- headers:请求头,支持鉴权
- chunkSize:切片大小,默认 5MB
- maxConcurrency:最大并发数,默认 4
- chunkRequestTimeout:单个切片请求超时时间(毫秒),默认 5000
- chunkRetryCount:单个切片最大重试次数,默认 3
- showUI:是否展示内置 UI,默认 true
- trigger:自定义上传入口,提供 open 与 upload 两个方法
- fileList:所有文件的上传状态列表
- getFileState:获取单文件状态(含 hash、进度、切片)
- getChunkState:获取单切片状态
后端需要实现以下 4 个接口(路径可通过 Props 自定义),并遵循约定的参数和返回格式。
#### 1. 校验接口 (Check)
- Method: GET{uploadUrl}{checkUrl}
- URL: (默认 /check)filename
- Query Parameters:
| 参数名 | 类型 | 说明 |
| :--- | :--- | :--- |
| | string | 原始文件名 |fileHash
| | string | 文件完整 MD5 值 |ext
| | string | 文件后缀名 (不含点) |
- Response (JSON):
`json`
{
"shouldUpload": boolean, // 是否需要上传(true: 需要上传/续传; false: 秒传完成)
"uploadedChunks": string[] // 已存在的切片Hash列表(用于断点续传)
}
{ data: { shouldUpload, uploadedChunks } }
_兼容格式_:也可以返回
#### 2. 切片上传接口 (Upload Chunk)
- Method: POST{uploadUrl}{uploadActionUrl}
- URL: (默认 /upload)multipart/form-data
- Content-Type: chunk
- Body Parameters:
| 参数名 | 类型 | 说明 |
| :--- | :--- | :--- |
| | Blob | 切片二进制数据 |chunkHash
| | string | 切片唯一标识 (目前使用切片索引) |fileHash
| | string | 文件完整 MD5 值 |index
| | string | 切片索引 (0, 1, 2...) |
- Response:
- HTTP Status 200 表示成功,Body 内容不限。
#### 3. 合并接口 (Merge)
- Method: POST{uploadUrl}{mergeUrl}
- URL: (默认 /merge)application/json
- Content-Type: fileHash
- Body:
- : string,文件完整 MD5 值ext
- : string,文件后缀名chunkSize
- : number,切片大小(字节)
- Response:
- 若不使用 SSE:HTTP 200 表示合并成功。
- 若使用 SSE:HTTP 200 仅表示请求已接收,合并结果通过 SSE 推送。
#### 4. SSE 监听接口 (Server-Sent Events)
- Method: GET{sseUrl}?file_hash=<文件完整 MD5>
- URL: file_hash
- 说明: 用于耗时合并任务的异步通知,前端在建立 SSE 时会自动在 URL 上附加 查询参数,后端需从该参数中获取文件标识。`
- Event Format(后端推送的数据结构):
json`
{
"meta": {
"type": "notify",
"biz": "upload",
"request_id": "",
"session_id": "",
"timestamp": 1234567890
},
"data": {
"event": "upload_complete",
"file_id": "任务唯一标识(UUID)",
"file_hash": "文件完整MD5",
"url": "文件访问地址",
"status": "completed" // 或 "failed"
}
}
#### 5. 取消接口 (Cancel)
- Method: POST{uploadUrl}{cancelUrl}
- URL: (默认 /cancel)application/json
- Content-Type: fileHash
- Body Parameters:
| 参数名 | 类型 | 说明 |
| :--- | :--- | :--- |
| | string | 文件完整 MD5 值 |filename` | string | 原始文件名 |
|
- Response:
- HTTP 200 表示取消请求已接收(无论后端是否真实存在该任务,均应返回成功以保证前端流程不中断)。
---