Vue3 + ElementPlus 前端实现分片上传

目录

1. 什么是分片上传

2. 上传组件模板

3. 上传组件逻辑

3.1 基本思路

3.2 选择上传文件

3.3 校验文件是否合法

3.4 文件加密

3.5 合并文件

3.6 文件切片上传

4. 参考文章

4.1 文章链接

4.2 参考文章提到的注意事项

4.2.1 nginx 上传大小限制

4.2.2 大文件下载


1. 什么是分片上传

将 一个文件 切割为 一系列特定大小的 数据片段,将这些 数据片段 分别上传到服务端;

全部上传完成后,再由服务端将这些 数据片段 合并成为一个完整的资源;

上传过程中,由于外部因素(比如网络波动)导致上传中断,下次上传时会保留该文件的上传进度(断点续传);

2. 上传组件模板

包含三部分:

  • 上传组件,使用 el-upload
  • 进度条组件,使用 el-progress
  • 上传完成状态组件,使用 el-input 自定义
 Drop file here or click to upload// 进度条// 上传成功之后隐藏上传文件组件

3. 上传组件逻辑

3.1 基本思路

使用 el-upload 选择文件

选择成功的 回调函数 可以读取文件信息,用于前端校验文件的合法性

前端校验文件合法后,将文件进行切片

通过 请求轮询 把切片传递给后端

3.2 选择上传文件

在这一步,可以获得文件信息

根据文件信息,对文件进行合法性校验

校验成功后,调用文件切片方法

/*** @description: 选择上传文件* @param file el-upload 返回的参数*/
const handleFileUpload = async (file: any) => {console.log('el-upload 返回的参数 === ', file.file);// 如果文件合法,则进行分片上传if (await checkMirrorFile(file)) {// 文件信息const files = file.file;// 从 0 开始的切片const shardIndex = 0;// 调用 文件切片 方法uploadFileSilce(files, shardIndex);// 文件非法,则进行提示} else {ElMessage.error('请检查文件是否合法!');}
};

3.3 校验文件是否合法

校验文件格式

校验文件大小

调用接口,校验磁盘剩余空间大小

/*** @description: 校验文件合法性*/
const checkMirrorFile = async (file) => {// 校验文件格式,支持.zip/.tarconst fileType = file.file.name.split('.')if (fileType[fileType.length - 1] !== 'zip' && fileType[fileType.length - 1] !== 'tar') {ElMessage.warning('文件格式错误,仅支持 .zip/.tar')return false}// 校验文件大小const fileSize = file.file.size;// 文件大小是否超出 2Gif (fileSize > 2 * 1024 * 1024 * 1024) {ElMessage.warning('上传文件大小不能超过 2G')return false}// 调用接口校验文件合法性,比如判断磁盘空间大小是否足够const res = await checkMirrorFileApi()if (res.code !== 200) {ElMessage.warning('暂时无法查看磁盘可用空间,请重试')return false}// 查看磁盘容量大小if (res.data.diskDevInfos && res.data.diskDevInfos.length > 0) {let saveSize = 0res.data.diskDevInfos.forEach(i => {// 磁盘空间赋值if (i.devName === '/dev/mapper/centos-root') {// 返回值为GB,转为字节BsaveSize = i.free * 1024 * 1024 * 1024}})// 上传的文件大小没有超出磁盘可用空间if (fileSize < saveSize) {return true} else {ElMessage.warning('文件大小超出磁盘可用空间容量')return false}} else {ElMessage.warning('文件大小超出磁盘可用空间容量')return false}
}

3.4 文件加密

此处文件上传用 MD5 进行加密,需要安装依赖 spark-md5

npm i spark-md5

/*** @description: 文件加密处理*/
const getMD5 = (file: any): Promise => new Promise((resolve, reject) => {const spark = new SparkMD5.ArrayBuffer();// 获取文件二进制数据const fileReader = new FileReader();fileReader.readAsArrayBuffer(file); // file 就是获取到的文件// 异步执行函数fileReader.addEventListener('load', (e: any) => {spark.append(e.target.result);const md5: string = spark.end();resolve(md5);});fileReader.addEventListener('error', (e) => {reject(e);});
});

3.5 合并文件

通过接口合并上传文件,接口需要的参数:

  • 文件名
  • 文件唯一 hash 值

接口合并完成后,前端展示已上传的文件名称

/*** @description: 合并文件* @param name 文件名* @param hash 文件唯一 hash 值* @return 命名名称*/
const composeFile = async (name: string, hash: string) => {console.log('开始文件合并');const res = await uploadFileMerge({applicationId: props.applicationId,applicationVersion: props.applicationVersion,bucketName: 'app',fileName: name,hash,});console.log('后端接口合并文件 ===', res);if (res.status === 200 && res.data.code) {// 合并成功后,调整已上传的文件名称state.editForm.inlineAppVersionModel.fileName = name;}
};

3.6 文件切片上传

接口轮询 —— 每次携带一个文件切片给后端;后端接受到切片 并 返回成功状态码后,再进行下一次切片上传

/*** @description: 分片函数* @param file 文件* @param shardIndex 分片数量*/
const uploadFileSilce = async (file: File, shardIndex: number) => {// 文件名const { name } = file;// 文件大小const { size } = file;// 分片大小const shardSize = 1024 * 1024 * 5;// 文件加密const hash: string = await getMD5(file);// 分片总数const shardTotal = Math.ceil(size / shardSize);// 如果 当前分片索引 大于 总分片数if (shardIndex >= shardTotal) {isAlive.value = false;progress.value = 100;// 合并文件composeFile(name, hash);return;}// 文件开始结束的位置const start = shardIndex * shardSize;const end = Math.min(start + shardSize, size);// 开始切割const packet = file.slice(start, end);// 拼接请求参数const formData = new FormData();formData.append('file', packet);formData.append('applicationId', props.applicationId);formData.append('applicationVersion', props.applicationVersion);formData.append('bucketName', 'app');formData.append('hash', hash);formData.append('shardSize', shardSize as unknown as string);formData.append('seq', shardIndex as unknown as string);// 如果 当前分片索引 小于 总分片数if (shardIndex < shardTotal) {// 进度条保留两位小数展示progress.value = Number(((shardIndex / shardTotal) * 100).toFixed(2)) * 1;// 调用文件上传接口const res = await uploadFile(formData);if (res.status !== 200) {ElMessage.error('上传失败');progress.value = 0;return;}if (res.status === 200 && res.data.code === 200) {// 这里为所有切片上传成功后进行的操作console.log('上传成功');}// eslint-disable-next-line no-param-reassignshardIndex++;// 递归调用 分片函数uploadFileSilce(file, shardIndex);}};

4. 参考文章

4.1 文章链接

前端大文件上传和下载(分片上传)_BreenCL的博客-CSDN博客_前端分片上传前端大文件上传(分片上传)一、问题日常业务中难免出现前端需要向后端传输大型文件的情况,这时单次的请求不能满足传输大文件的需求,就需要用到分片上传业务需求为:用户可以上传小于20G的镜像文件,并进显示当前上传进度前端:vue3.x+Element Plus组件+axios二、解决解决思路简单为前端选择文件后读取到文件的基本信息,包括:文件的大小、文件格式等信息,用于前端校验,校验完成后将文件进行切片并通过请求轮询把切片传递给后端Vue的元素代码如下,主要借助el-upload组件:&lhttps://blog.csdn.net/baoyin0822/article/details/123922628

4.2 参考文章提到的注意事项

4.2.1 nginx 上传大小限制

nginx 默认上传大小为 1MB,若超过 1MB,则需要修改 nginx 配置 解除上传限制

4.2.2 大文件下载

/*** @description: 动态创建 a 标签,实现大文件下载*/
const downloadMirror = async (item) => {let t = {id: item.id,}const res = await downloadMirrorApi(t)if (res.headers["content-disposition"]) {let temp = res.headers["content-disposition"].split(";")[1].split("filename=")[1]let fileName = decodeURIComponent(temp)// 通过创建a标签实现文件下载let link = document.createElement('a')link.download = fileNamelink.style.display = 'none'link.href = res.data.msgdocument.body.appendChild(link)link.click()document.body.removeChild(link)} else {ElMessage({message: '该文件不存在',type: 'warning',})}
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部