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:这个是将上传文件成功后的各分片最后合并的接口