Appearance
BizChunkUpload 分片上传
大文件分片上传(断点续传):init 探测 → chunk 并发单片 → finish 合并 三段契约(协议由业务侧对接后端,组件零接口)。init 返回已传分片即续传、返回完整地址即秒传;hash prop 可注入文件指纹;暂停 / 继续 / 取消 / 失败重试(重试只发未完成分片)。
基础用法
大文件自动切片并发上传;暂停后继续只传未完成分片(断点续传)
chunk-size 切片、concurrent 并发池;暂停后继续只传未完成分片。<script setup lang="ts">
/** 基础用法:mock 三段契约(分片 5MB → 并发 3 → 合并),演示进度/暂停/续传 */
import { ref } from 'vue'
import { BizChunkUpload } from 'vue3-ep-biz-lib'
const url = ref('')
/** 服务端已收分片(mock 断点续传记忆) */
const received = new Set<number>()
const request = {
init: async () => ({ uploaded: [...received] }),
chunk: async (blob: Blob, meta: { index: number; chunkCount: number }) => {
// mock 网络耗时与分片大小成正比
await new Promise((resolve) => setTimeout(resolve, 200 + blob.size / 2000))
received.add(meta.index)
},
finish: async (meta: { name: string }) => {
await new Promise((resolve) => setTimeout(resolve, 600))
return `https://cdn.example.com/files/${encodeURIComponent(meta.name)}`
},
}
</script>
<template>
<div>
<BizChunkUpload
v-model="url"
:request="request"
:chunk-size="5"
:concurrent="3"
tip="大文件自动切片并发上传;暂停后继续只传未完成分片(断点续传)"
/>
<div class="tip" v-if="url !== ''">上传完成:{{ url }}</div>
</div>
</template>
<style scoped>
.tip {
margin-top: 8px;
font-size: 13px;
color: var(--el-text-color-secondary);
word-break: break-all;
}
</style>
显示代码
秒传 · 失败重试
init 返回
fileUrl 直接完成(同名命中秒传);首片 mock 失败演示错误态与重试续传。<script setup lang="ts">
/** 进阶:秒传探测(init 返回 fileUrl 直接完成)+ 失败重试演示 */
import { ElMessage } from 'element-plus'
import { ref } from 'vue'
import { BizChunkUpload } from 'vue3-ep-biz-lib'
const url = ref('')
let firstChunkFailed = false
const request = {
init: async (meta: { name: string; hash?: string }) => {
await new Promise((resolve) => setTimeout(resolve, 400))
// 同名同 hash 命中「秒传」
if (meta.hash === 'demo-fast-hash' || meta.name === '已存在.mp4') {
return { fileUrl: 'https://cdn.example.com/files/exist.mp4' }
}
return { uploaded: [] }
},
chunk: async (blob: Blob, meta: { index: number }) => {
await new Promise((resolve) => setTimeout(resolve, 300))
// 首片 mock 一次失败,演示错误态与重试(重试跳过已传片)
if (meta.index === 0 && !firstChunkFailed) {
firstChunkFailed = true
throw new Error('分片 0 上传超时(mock)')
}
void blob
},
finish: async () => 'https://cdn.example.com/files/new-upload.mp4',
}
const onError = (message: string): void => {
ElMessage.error(message)
}
</script>
<template>
<BizChunkUpload
v-model="url"
:request="request"
:chunk-size="1"
button-text="上传视频(可秒传)"
@error="onError"
>
</BizChunkUpload>
</template>
显示代码
指纹秒传
hash prop 注入指纹计算,init 命中已存在文件 0 分片完成。
<script setup lang="ts">
/** hash 注入:指纹相同 → init 直接秒传 */
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { BizChunkUpload } from 'vue3-ep-biz-lib'
const url = ref('')
/** 模拟已存在的文件指纹 */
const EXISTING = 'f8a3c2d1b9e64f00'
const hash = async (): Promise<string> => {
await new Promise((resolve) => setTimeout(resolve, 400))
return EXISTING
}
const request = {
init: async (meta: { hash?: string }) => {
await new Promise((resolve) => setTimeout(resolve, 300))
if (meta.hash === EXISTING) return { fileUrl: 'https://cdn.example.com/files/exist.bin' }
return { uploaded: [] }
},
chunk: async () => undefined,
finish: async () => 'https://cdn.example.com/files/new.bin',
}
const onSuccess = (u: string): void => {
ElMessage.success(`秒传命中:${u}(0 分片上传)`)
}
</script>
<template>
<BizChunkUpload v-model="url" :request="request" :hash="hash" button-text="上传(指纹秒传)" @success="onSuccess" />
</template>
显示代码
外部控制
外部读取进度:0%
上传中点暂停 / 继续 / 取消,进度面板状态同步变化(选择 >1MB 文件体验分片过程)
expose pause/resume/cancel + percent 实时读取(自绘控制条场景)。
<script setup lang="ts">
/** 外部控制:expose pause/resume/cancel + 实时进度读取 */
import { computed, ref } from 'vue'
import { BizChunkUpload } from 'vue3-ep-biz-lib'
import type { BizChunkUploadInstance } from 'vue3-ep-biz-lib'
const upRef = ref<BizChunkUploadInstance | null>(null)
const url = ref('')
/** mock:每片 200ms,共 12 片(chunk-size 1MB 时小文件也按 12 片演示需要大文件——用 maxSize 限制即可,这里直接演示 API) */
const request = {
chunk: async (_blob: Blob, meta: { index: number }) => {
await new Promise((resolve) => setTimeout(resolve, 200))
void meta
},
finish: async () => 'https://cdn.example.com/files/manual.bin',
}
const percent = computed(() => upRef.value?.percent ?? 0)
</script>
<template>
<div>
<BizChunkUpload ref="upRef" v-model="url" :request="request" :chunk-size="1" button-text="选择文件" />
<div class="ops">
<ElButton size="small" @click="upRef?.pause()">pause()</ElButton>
<ElButton size="small" @click="upRef?.resume()">resume()</ElButton>
<ElButton size="small" type="danger" @click="upRef?.cancel()">cancel()</ElButton>
<span class="tip">外部读取进度:{{ percent }}%</span>
</div>
<div class="tip2">上传中点暂停 / 继续 / 取消,进度面板状态同步变化(选择 >1MB 文件体验分片过程)</div>
<ElAlert
v-if="url !== ''"
type="success"
:closable="false"
:title="`完成:${url}`"
style="margin-top: 8px"
/>
</div>
</template>
<style scoped>
.ops {
display: flex;
align-items: center;
gap: 8px;
margin-top: 10px;
}
.tip {
font-size: 13px;
color: var(--el-text-color-secondary);
}
.tip2 {
margin-top: 6px;
font-size: 12px;
color: var(--el-text-color-secondary);
}
</style>
显示代码
并发对比
并发数: 每片 mock 300ms:同样片数下总耗时随并发显著下降(选择 >1MB 文件体验)
concurrent 1/3/6 切换体验吞吐差异(网络窗口与后端压力权衡)。
<script setup lang="ts">
/** 并发对比:concurrent 1 / 3 / 6 依次体验分片吞吐(每片 mock 300ms) */
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { BizChunkUpload } from 'vue3-ep-biz-lib'
const concurrent = ref(3)
const url = ref('')
const request = {
chunk: async (_blob: Blob, meta: { index: number }) => {
await new Promise((resolve) => setTimeout(resolve, 300))
void meta
},
finish: async () => 'https://cdn.example.com/files/cc.bin',
}
const onSuccess = (): void => {
ElMessage.success(`并发 ${concurrent.value} 上传完成`)
}
</script>
<template>
<div>
<div class="ops">
并发数:
<ElRadioGroup v-model="concurrent">
<ElRadioButton :value="1">1</ElRadioButton>
<ElRadioButton :value="3">3</ElRadioButton>
<ElRadioButton :value="6">6</ElRadioButton>
</ElRadioGroup>
<span class="tip">每片 mock 300ms:同样片数下总耗时随并发显著下降(选择 >1MB 文件体验)</span>
</div>
<BizChunkUpload v-model="url" :request="request" :concurrent="concurrent" :chunk-size="1" @success="onSuccess" />
</div>
</template>
<style scoped>
.ops {
display: flex;
align-items: center;
gap: 10px;
margin-bottom: 12px;
font-size: 13px;
}
.tip {
font-size: 12px;
color: var(--el-text-color-secondary);
}
</style>
显示代码
表单集成
el-form 必填 + 上传完成才可提交(在线课程大附件发布)。
<script setup lang="ts">
/** 表单集成:视频必填 + 上传完成才能提交(大附件表单) */
import { reactive, ref } from 'vue'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { BizChunkUpload } from 'vue3-ep-biz-lib'
const formRef = ref<FormInstance>()
const form = reactive({ title: '', file: '' })
const rules: FormRules = {
title: [{ required: true, message: '请填写课程名', trigger: 'blur' }],
file: [{ required: true, message: '请上传正片', trigger: 'change' }],
}
const request = {
chunk: async () => undefined,
finish: async () => 'https://cdn.example.com/courses/lesson-01.mp4',
}
const submit = (): void => {
void formRef.value?.validate((valid) => {
if (valid) ElMessage.success(`发布课程:${form.title}(正片 ${form.file})`)
})
}
</script>
<template>
<ElForm ref="formRef" :model="form" :rules="rules" label-width="80px" style="max-width: 480px">
<ElFormItem label="课程名" prop="title">
<ElInput v-model="form.title" placeholder="第 01 讲" />
</ElFormItem>
<ElFormItem label="正片" prop="file">
<BizChunkUpload v-model="form.file" :request="request" :chunk-size="1" button-text="分片上传正片" />
</ElFormItem>
<ElFormItem>
<ElButton type="primary" :disabled="form.file === ''" @click="submit">发布</ElButton>
</ElFormItem>
</ElForm>
</template>
显示代码
Props
| 属性名 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| model-value / v-model | string | '' | 上传完成后的文件地址 |
| request | BizChunkUploadRequest | — | 必填。init(可选探测)/ chunk / finish 三段契约 |
| chunk-size | number | 5 | 单片大小(MB) |
| max-size | number | 2048 | 大小上限(MB) |
| concurrent | number | 3 | 并发上传分片数 |
| accept | string | — | 接受的文件类型 |
| hash | (file: File) => Promise<string> | — | 文件指纹(秒传判定;未注入 meta 无 hash) |
| tip / button-text | string | '' / 上传大文件 | 提示与按钮文案 |
BizChunkUploadRequest:init?(meta) => Promise<{ uploaded?: number[]; fileUrl?: string }>、chunk(blob, meta & { index }) => Promise<void>、finish(meta) => Promise<string>;BizChunkMeta 含 name / size / chunkSize / chunkCount / hash?。
Events
| 事件名 | 参数 | 说明 |
|---|---|---|
| update:model-value | (url: string) | 地址变化 |
| success | (url: string) | 上传完成(含秒传) |
| error | (message: string) | 上传失败 |
Slots
| 插槽名 | 说明 |
|---|---|
| — | 无插槽 |
Expose
| 属性/方法 | 说明 |
|---|---|
| pause / resume / retry / cancel | 暂停 / 继续 / 重试 / 取消重置 |
| percent / uploadedCount | 进度百分比 / 已传分片数 |
注意:
request为 Promise 注入,无法真正中断在途请求——暂停语义为停发后续分片(在途分片自然完成)。