一、前言
上传是"表单之外"的文件流场景:头像、Excel 导入、附件、视频。本篇四连:el-upload 基础与校验 → 自定义上传对接 062 篇 api → 头像即时预览 → 大文件分片上传思路。
二、el-upload 核心认知
el-upload 的两个工作模式:
text
① 自动上传:选中文件立即发请求(action 指定接口)
② 手动上传:auto-upload=false → 用户点"提交"时才一起发(表单场景)
★ 企业真实做法:不用 action(因为要带 token/统一响应处理)
用 :http-request 覆盖默认上传 → 内部调 062 篇的 api
关键 props 速查:
| props | 作用 |
|---|---|
| action | 上传地址(自定义上传时随便填个 # 也要写,组件要求必传) |
| :auto-upload | 选完是否立即传 |
| :before-upload | 上传前校验,返回 false / Promise.reject 可拦截 |
| :show-file-list | 显示已上传文件列表 |
| :limit / :on-exceed | 数量限制 / 超出回调 |
| :http-request | 自定义上传函数(企业标配) |
| :on-success / :on-error | 成功/失败回调(action 模式用) |
三、完整可运行代码:头像上传(校验 + 预览 + 自定义上传)
vue
<!-- src/views/AvatarUploadView.vue -->
<template>
<el-upload
:show-file-list="false"
:before-upload="beforeUpload"
:http-request="customUpload"
accept="image/png, image/jpeg"
>
<el-avatar :size="80" :src="previewUrl">
{{ previewUrl ? '' : '上传' }}
</el-avatar>
<p>点击头像更换(png/jpg,≤ 2MB)</p>
</el-upload>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import type { UploadRequestOptions } from 'element-plus'
import { uploadAvatar } from '@/api/upload' // 062 篇的 api 模块
const previewUrl = ref('')
// ① 上传前校验:类型 + 大小(双保险,accept 只是"文件选择器提示")
function beforeUpload(file: File) {
const isImage = ['image/png', 'image/jpeg'].includes(file.type)
const isLt2M = file.size / 1024 / 1024 < 2
if (!isImage) {
ElMessage.error('只能上传 png/jpg 图片')
return false // ★ 返回 false 拦截上传
}
if (!isLt2M) {
ElMessage.error('图片不能超过 2MB')
return false
}
return true
}
// ② 自定义上传:内部走统一的 request(token、错误提示全自动,060 篇白封装了)
async function customUpload(options: UploadRequestOptions) {
const fd = new FormData()
fd.append('file', options.file) // ★ 文件进 FormData,字段名和后端对齐
const res = await uploadAvatar(fd) // request.post('/upload/avatar', fd)
if (res.code === 0) {
previewUrl.value = res.data.url // 服务器返回的图片地址
ElMessage.success('头像更新成功')
}
}
</script>
ts
// src/api/upload.ts
import { post } from '@/utils/request'
/** 上传:Content-Type 交给浏览器自动设置 multipart 边界,request 里别手动写死 */
export function uploadAvatar(data: FormData) {
return post<{ url: string }>('/upload/avatar', data)
}
【截图位置:点击头像选图 → 校验失败红提示 / 成功后头像刷新】
本地预览技巧(上传前就看到效果,弱网体验好):
ts
// file.raw 是原生 File 对象(el-upload 包装层里的)
const url = URL.createObjectURL(options.file)
// ★ 用完/替换时释放,防内存泄漏
URL.revokeObjectURL(oldUrl)
四、手动上传模式:表单一起提交
附件场景:文件选了先"暂存",和表单一起提交:
vue
<el-upload :auto-upload="false" :limit="3" :on-exceed="onExceed" v-model:file-list="fileList">
<el-button>选择附件(最多 3 个)</el-button>
</el-upload>
<el-button type="primary" @click="onSubmitAll">提交</el-button>
ts
const fileList = ref<any[]>([])
async function onSubmitAll() {
const fd = new FormData()
fd.append('title', title.value)
fileList.value.forEach((f) => fd.append('files', f.raw)) // 多文件同名数组
const res = await post('/article/create', fd)
// 成功后清空列表
fileList.value = []
}
五、大文件分片上传思路(概念 + 骨架代码)
500MB 视频:单个请求超时、失败从头再来。思路 = 切片 + 并发 + 合并:
text
1. 前端切片:file.slice(0, 5MB)、file.slice(5MB, 10MB)...(File 继承 Blob 自带 slice)
2. 计算文件 hash(如 spark-md5)作为唯一标识 → 询问后端"传过没"(秒传/断点续传)
3. 并发上传所有分片(带 index 序号)
4. 全部完成后通知后端按序合并
ts
// 切片骨架(面试常考思路)
async function chunkUpload(file: File, CHUNK_SIZE = 5 * 1024 * 1024) {
const chunks = Math.ceil(file.size / CHUNK_SIZE)
const tasks: Promise<unknown>[] = []
for (let i = 0; i < chunks; i++) {
const blob = file.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE)
const fd = new FormData()
fd.append('chunk', blob)
fd.append('index', String(i))
fd.append('hash', await getFileHash(file)) // spark-md5 计算
tasks.push(post('/upload/chunk', fd)) // 进阶:并发控制(3 个一批)
}
await Promise.all(tasks)
await post('/upload/merge', { hash: await getFileHash(file), chunks })
}
关键词记牢:slice 切片、hash 秒传、断点续传(跳过已传分片)、并发控制------面试和实战都够用,完整实现属后端配合的进阶课题。
六、踩坑记录
- FormData 被 request 默认转 JSON :axios 对 FormData 会自动设 multipart 边界------别手动设 Content-Type,手动写 application/json 文件就废了
- before-upload 返回 Promise.reject 但没提示:拦截了用户却不知道原因------reject 前先 ElMessage
- accept 以为能拦住非法文件:accept 只是文件选择器的过滤提示,拖拽/改后缀可绕过------before-upload 里再校验一次
- 对象 URL 不释放:createObjectURL 多次调用内存暴涨------替换/卸载时 revokeObjectURL
- 多文件上传数量失控:没配 :limit 和 :on-exceed------两者成对配置
- 分片并发拉满:200 个分片同时发,浏览器连接耗尽反而慢------并发控制(每批 3~6 个,Promise 池)
- 上传中页面关闭/刷新:进度丢失且服务端留垃圾分片------beforeunload 提示 + 后端分片过期清理
七、今日小结
- el-upload 企业姿势::http-request 自定义上传,内部走统一 request(token/错误全复用)
- 校验双保险:accept 提示 + before-upload 真拦截(类型 + 大小)
- 表单场景:auto-upload=false 暂存 file-list,提交时进 FormData
- 大文件关键词:slice 切片 → hash 秒传 → 并发上传 → 后端合并
下篇预告
后台做多了,试试移动端?下一篇 083 Vue3 移动端初探:Vant 4 按需引入 + 移动端适配方案(vw/rem),一套组件库和 PC 的区别讲清楚。