vue-simple-uploader 是一个基于 Vue.js 的简单文件上传插件,可支持文件分片上传、断点续传以及秒传,有以下几个特点:
1、简单易用:只需在 Vue 组件中使用该插件,并设置相应的配置选项即可实现文件上传功能。
2、多文件上传:插件支持同时上传多个文件,可以选择并一次性上传多个文件。
3、进度条显示:该插件提供了进度条显示上传进度,让用户清楚地知道文件上传的状态和进度。
4、上传前验证:可以通过设置验证规则,对上传的文件进行前端验证。例如,限制文件类型、大小等,以确保只上传符合要求的文件。
5、自定义选项:插件提供了一些可自定义的选项,如上传地址、请求头部、参数等,以满足不同的业务需求。
6、事件回调:提供了丰富的事件回调函数,允许在不同的上传阶段执行自定义逻辑,如上传前、上传成功、上传失败等。
总之,Vue Simple Uploader 简化了文件上传的过程,并提供了一些有用的功能和灵活的配置选项,易于集成和使用。
最终成功效果
1、首先需要下载依赖,vue-simple-uploader、spark-md5
npm i --save vue-simple-uploader@next spark-md5
下载好的版本号是1.0.1,这个版本是支持vue3的,不加@next 默认是0.7.6,是只支持vue2的,vue3中使用会报错。
2、main.js 注册
import { createApp } from 'vue'; import App from './App.vue'; // 文件分片上传 import uploader from 'vue-simple-uploader'; import 'vue-simple-uploader/dist/style.css'; createApp(App).use(uploader).mount('#app');
复制
3、编写文件类型 accept.config.js
export const ACCEPT_CONFIG = { image: ['.png', '.jpg', '.jpeg', '.gif', '.bmp'], video: ['.mp4', '.rmvb', '.mkv', '.wmv', '.flv'], document: [ '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.pdf', '.txt', '.tif', '.tiff', '.rar', '.zip', ], getAll() { return [...this.image, ...this.video, ...this.document]; }, };
复制
4、编写upload.vue
<template> <div> <!-- 上传器 --> <uploader ref="uploaderRef" :options="options" :autoStart="false" :file-status-text="fileStatusText" class="uploader-ui" @file-added="onFileAdded" @file-success="onFileSuccess" @file-progress="onFileProgress" @file-error="onFileError" > <uploader-unsupport></uploader-unsupport> <uploader-drop> <div> <uploader-btn id="global-uploader-btn" ref="uploadBtn" :attrs="attrs" >选择文件<i class="el-icon-upload el-icon--right"></i ></uploader-btn> </div> </uploader-drop> <uploader-list></uploader-list> </uploader> </div> </template> <script setup> import { ACCEPT_CONFIG } from '@/config/accept.config'; import { reactive, ref } from 'vue'; import SparkMD5 from 'spark-md5'; import { mergeFile } from '@/api/demo/fileUpload/index'; import { ElMessage } from 'element-plus'; const options = reactive({ //目标上传 URL,默认POST, import.meta.env.VITE_API_URL = api // target ==》http://localhost:6666/api/uploader/chunk target: import.meta.env.VITE_API_URL + '/uploader/chunk', query: {}, headers: { // 需要携带token信息,当然看各项目情况具体定义 token: "your_token", }, //分块大小(单位:字节) chunkSize: '2048000', //上传文件时文件内容的参数名,对应chunk里的Multipart对象名,默认对象名为file fileParameterName: 'upfile', //失败后最多自动重试上传次数 maxChunkRetries: 3, //是否开启服务器分片校验,对应GET类型同名的target URL testChunks: true, // 服务器分片校验函数 checkChunkUploadedByResponse: function (chunk, response_msg) { let objMessage = JSON.parse(response_msg); console.log(response_msg, 'response_msg'); if (objMessage.skipUpload) { return true; } return (objMessage.uploadedChunks || []).indexOf(chunk.offset + 1) >= 0; }, }); const attrs = reactive({ accept: ACCEPT_CONFIG.getAll(), }); const fileStatusText = reactive({ success: '上传成功', error: '上传失败', uploading: '上传中', paused: '暂停', waiting: '等待上传', }); onMounted(() => { console.log(uploaderRef.value, 'uploaderRef.value'); }); function onFileAdded(file) { computeMD5(file); } function onFileSuccess(rootFile, file, response, chunk) { //refProjectId为预留字段,可关联附件所属目标,例如所属档案,所属工程等 file.refProjectId = '123456789'; mergeFile(file) .then((responseData) => { if (responseData.data.code === 415) { console.log('合并操作未成功,结果码:' + responseData.data.code); } ElMessage.success(responseData.data); }) .catch(function (error) { console.log('合并后捕获的未知异常:' + error); }); } function onFileError(rootFile, file, response, chunk) { console.log('上传完成后异常信息:' + response); } /** * 计算md5,实现断点续传及秒传 * @param file */ function computeMD5(file) { file.pause(); //单个文件的大小限制2G let fileSizeLimit = 2 * 1024 * 1024 * 1024; console.log('文件大小:' + file.size); console.log('限制大小:' + fileSizeLimit); if (file.size > fileSizeLimit) { file.cancel(); } let fileReader = new FileReader(); let time = new Date().getTime(); let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice; let currentChunk = 0; const chunkSize = 10 * 1024 * 1000; let chunks = Math.ceil(file.size / chunkSize); let spark = new SparkMD5.ArrayBuffer(); //由于计算整个文件的Md5太慢,因此采用只计算第1块文件的md5的方式 let chunkNumberMD5 = 1; loadNext(); fileReader.onload = (e) => { spark.append(e.target.result); if (currentChunk < chunkNumberMD5) { loadNext(); } else { let md5 = spark.end(); file.uniqueIdentifier = md5; file.resume(); console.log( `MD5计算完毕:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${ file.size } 用时:${new Date().getTime() - time} ms` ); } }; fileReader.onerror = function () { error(`文件${file.name}读取出错,请检查该文件`); file.cancel(); }; function loadNext() { let start = currentChunk * chunkSize; let end = start + chunkSize >= file.size ? file.size : start + chunkSize; fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end)); currentChunk++; console.log('计算第' + currentChunk + '块'); } } const uploaderRef = ref(); function close() { uploaderRef.value.cancel(); } function error(msg) { console.log(msg, 'msg'); } </script> <style scoped> .uploader-ui { padding: 15px; margin: 40px auto 0; font-size: 12px; font-family: Microsoft YaHei; box-shadow: 0 0 10px rgba(0, 0, 0, 0.4); } .uploader-ui .uploader-btn { margin-right: 4px; font-size: 12px; border-radius: 3px; color: #fff; background-color: #409eff; border-color: #409eff; display: inline-block; line-height: 1; white-space: nowrap; } .uploader-ui .uploader-list { max-height: 440px; overflow: auto; overflow-x: hidden; overflow-y: auto; } </style>
复制
5、这是最后合并需要的接口 mergeFile,这个看各项目是怎么封装request,下面只是个例子
import request from '@/utils/request'; export const mergeFile = (data) => { return request({ url: '/uploader/mergeFile', method: 'POST', data, }); };
复制
6、使用
<template> <div> <UploadVue /> </div> </template> <script setup> import UploadVue from './Upload.vue'; </script>
复制
上传文件后端一共需要写三个接口,其中给到前端只需要两个即可,如下:
1、/uploader/chunk:upload.vue文件的options对象下的target,这里需要注意接口携带token的问题,也有些项目是cookie,还有就是跨域的问题,也就是为什么要用import.meta.env.VITE_API_URL进行拼接的原因,本地联调的话因为VITE_APP_URL环境变量设置的是 /api ,所以是http://localhost:6666/api/uploader/chunk
2、/uploader/mergeFile:这个是将上传文件成功后的各分片最后合并的接口