jhl-large-file-upload
v1.0.2
Published
本项目基于 Vue 3 + TypeScript + Vite,目标是沉淀一个性能好、扩展性高、稳定性强的大文件上传前端基础设施,既可以以 npm 插件方式接入现有项目,也提供开箱即用的 Demo 页面便于调试与验证。
Readme
Vue 大文件分片上传组件(Vue 3 + TypeScript + Vite)
本项目基于 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 包发布
上传工作流(前端视角)
- 选择文件:通过默认按钮或插槽自定义入口触发原生
<input type="file"> - 文件切片:按配置的切片大小将文件切分为多个 Blob,对每个切片建立上传任务
- Hash 计算:在 Web Worker 中对所有切片进行 Hash 计算,避免阻塞主线程,并持续回传进度
- 上传前校验:调用后端校验接口,判断是否需要上传以及哪些切片已存在
- 分片上传:根据校验结果,仅上传缺失的切片,并按最大并发数调度请求,实时更新进度和状态
- 合并请求:所有切片上传完成后,向后端发送合并请求,必要时通过 SSE 监听合并结果
- 状态管理:在上传过程中维护文件级和切片级状态,支持暂停、恢复、取消以及记录删除等操作
Demo 项目运行说明
当前仓库中的 client 目录既包含上传组件实现,也包含一个 Demo 页面,方便在本地直接体验大文件上传流程:
- 开发调试:在
client目录执行npm install后运行npm run dev,打开浏览器访问本地地址即可 - 构建产物:执行
npm run build生成构建结果,可用于后续封装发布或集成到其他系统 - 预览构建:执行
npm run preview本地预览 build 后的资源
作为 npm 插件的使用方式
1. 安装
npm i jhl-large-file-upload2. 组件注册
全局注册
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");局部注册
import { Upload } from "jhl-large-file-upload";
import "jhl-large-file-upload/dist/style.css";3. 基本用法(带 UI)
<template>
<Upload
:upload-url="uploadUrl"
:sse-url="sseUrl"
:headers="headers"
:chunk-size="chunkSize"
:max-concurrency="maxConcurrency"
:show-ui="true"
/>
</template>
<script setup lang="ts">
import { Upload } from "jhl-large-file-upload";
import "jhl-large-file-upload/dist/style.css";
const uploadUrl = "http://localhost:8080";
const sseUrl = "http://localhost:8080/events";
const headers = {
Authorization: "Bearer token",
};
const chunkSize = 5 * 1024 * 1024;
const maxConcurrency = 4;
</script>4. 自定义入口(插槽)
<template>
<Upload :upload-url="uploadUrl" :show-ui="false">
<template #trigger="{ open, upload }">
<button type="button" @click="open">选择文件</button>
<input type="file" multiple @change="upload" />
</template>
</Upload>
</template>5. 方法与数据暴露
import { ref } from "vue";
import type { UploadExpose } from "@your-org/vue-large-upload";
const uploaderRef = ref<UploadExpose | null>(null);
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;6. 事件流与回调
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)
6. Props 说明
- uploadUrl:上传服务基础地址(baseURL)
- checkUrl:校验接口地址,默认 /check
- uploadActionUrl:上传分片接口地址,默认 /upload
- mergeUrl:合并接口地址,默认 /merge
- cancelUrl:取消接口地址,默认 /cancel
- sseUrl:SSE 地址,用于监听合并完成
- headers:请求头,支持鉴权
- chunkSize:切片大小,默认 5MB
- maxConcurrency:最大并发数,默认 4
- chunkRequestTimeout:单个切片请求超时时间(毫秒),默认 5000
- chunkRetryCount:单个切片最大重试次数,默认 3
- showUI:是否展示内置 UI,默认 true
7. Slot 说明
- trigger:自定义上传入口,提供 open 与 upload 两个方法
8. 数据结构说明
- fileList:所有文件的上传状态列表
- getFileState:获取单文件状态(含 hash、进度、切片)
- getChunkState:获取单切片状态
9. 后端接口对接文档
后端需要实现以下 4 个接口(路径可通过 Props 自定义),并遵循约定的参数和返回格式。
1. 校验接口 (Check)
Method:
GETURL:
{uploadUrl}{checkUrl}(默认/check)Query Parameters: | 参数名 | 类型 | 说明 | | :--- | :--- | :--- | |
filename| string | 原始文件名 | |fileHash| string | 文件完整 MD5 值 | |ext| string | 文件后缀名 (不含点) |Response (JSON):
{ "shouldUpload": boolean, // 是否需要上传(true: 需要上传/续传; false: 秒传完成) "uploadedChunks": string[] // 已存在的切片Hash列表(用于断点续传) }兼容格式:也可以返回
{ data: { shouldUpload, uploadedChunks } }
2. 切片上传接口 (Upload Chunk)
Method:
POSTURL:
{uploadUrl}{uploadActionUrl}(默认/upload)Content-Type:
multipart/form-dataBody Parameters: | 参数名 | 类型 | 说明 | | :--- | :--- | :--- | |
chunk| Blob | 切片二进制数据 | |chunkHash| string | 切片唯一标识 (目前使用切片索引) | |fileHash| string | 文件完整 MD5 值 | |index| string | 切片索引 (0, 1, 2...) |Response:
- HTTP Status 200 表示成功,Body 内容不限。
3. 合并接口 (Merge)
Method:
POSTURL:
{uploadUrl}{mergeUrl}(默认/merge)Content-Type:
application/jsonBody:
fileHash: string,文件完整 MD5 值ext: string,文件后缀名chunkSize: number,切片大小(字节)
Response:
- 若不使用 SSE:HTTP 200 表示合并成功。
- 若使用 SSE:HTTP 200 仅表示请求已接收,合并结果通过 SSE 推送。
4. SSE 监听接口 (Server-Sent Events)
- Method:
GET - URL:
{sseUrl}?file_hash=<文件完整 MD5> - 说明: 用于耗时合并任务的异步通知,前端在建立 SSE 时会自动在 URL 上附加
file_hash查询参数,后端需从该参数中获取文件标识。 - Event Format(后端推送的数据结构):
{ "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:
POSTURL:
{uploadUrl}{cancelUrl}(默认/cancel)Content-Type:
application/jsonBody Parameters: | 参数名 | 类型 | 说明 | | :--- | :--- | :--- | |
fileHash| string | 文件完整 MD5 值 | |filename| string | 原始文件名 |Response:
- HTTP 200 表示取消请求已接收(无论后端是否真实存在该任务,均应返回成功以保证前端流程不中断)。
