<div id="app">
<el-upload
class="upload-demo"
drag
multiple
limit="5"
:file-list="fileList"
:on-exceed="handleExceed"
:on-change="handleChange"
:before-upload="handleBeforeUpload"
:http-request="uploadCustomRequest" // 自定义上传
:http-request="uploadCustomRequestBlock" // 自定义分块上传
:on-progress="handleProgress"
:on-success="handleSuccess"
:on-error="handleError"
:on-preview="handlePreview"
:before-remove="handleBeforeRemove"
:on-remove="handleRemove">
<el-button size="small" type="primary">点击上传</el-button>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过500kb</div>
</el-upload>
</div>
<script>
import axios from "axios"; // 引入 axios,以便获取 axios 取消请求对象
export default{
data(){
return {
fileList: [], // 上传成功的文件和回显的文件
cancelToken: [], // 每个上传文件的请求的 cancelToken
messageInstance: null, // message 提示信息的实例
}
},
methods:{
/**
* 文件超出个数限制时的钩子
* @param {Object} files
* @param {Object} fileList
*/
handleExceed(files, fileList){
this.$message.warning(`当前限制选择 5个文件,本次选择了 ${ files.length } 个文件,共选择了 ${ files.length + fileList.length } 个文件`);
},
/**
* 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
* @param {Object} file
* @param {Object} fileList
*/
handleChange(file, fileList){
// 是否全部文件上传成功
let flag = fileList.every(item => item.status == 'success');
// 全部文件上传成功,关闭 “上传中” 提示
if(flag){
this.messageInstance.close();
this.messageInstance = null;
}
},
/**
* 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传
* @param {Object} file
*/
handleBeforeUpload(file){
// 验证文件类型
const isAcceptType = /\.(txt|doc|docx|xls|xlsx|wps|pdf|jar|war|zip|rar|tar|gz)$/.test(file.name.substring(file.name.lastIndexOf('.')));
// 验证文件大小(1G),file.size 单位为 B,除以 1024 变为 KB,再除以 1024 变为 MB
const isAcceptSize = file.size / 1024 / 1024 <= 1024;
if (!isAcceptType) {
this.$message.warning({
dangerouslyUseHTMLString: true,
message: '<p style="white-space: nowrap;">只能上传.txt、.doc、.docx、.xls,.xlsx、.wps、.pdf、.jar、.war、.zip、.rar、.tar、.gz格式的文件!</p>'
});
} else if (!isAcceptSize) {
this.$message.warning('上传的文件过大!');
}
// 如果符合文件类型及大小,则验证文件名是否重名
if(isAcceptType && isAcceptSize){
this.$axios.checkUploadFileDuplicateName({
fileName: info.file.name //文件名称
}).then(res => {
if(res.code == 0){
return true; // 允许上传
}else{
this.$message.error('文件名重复,请确认该文件是否已存在');
return false; // 不允许上传
}
})
}
// return isAcceptType && isAcceptSize; // 如果不需要验证重名,则使用此代码
},
/**
* 自定义上传
* @param {Object} info
*/
uploadCustomRequest(info){
const CancelToken = axios.CancelToken; // 获取 axios 取消请求对象
// 请求配置
let config = {
url: '/api/door/upload', // url、method、data 属性可以不写在此处,根据自己的 axios 封装方案
method: 'post',
data: {
file: info.file, // 上传的文件
hash: this.$uuid.v1(), // 生成的uuid(安装 vue-uuid 插件)
chunkNumber: 0
},
timeout: 0,
headers: {
'Content-Type': 'multipart/form-data',
},
cancelToken: new CancelToken(c => {
// this.cancelToken = c; // 单文件上传
this.cancelToken.push({ // 多文件上传
uid: info.file.uid,
cancelToken: c
})
}),
// 上传处理进度事件
onUploadProgress: progressEvent => {
// 获取当前上传进度
let complete = (progressEvent.loaded / progressEvent.total * 100 | 0);
let progress = { percent: complete };
info.onProgress(progress); // 调用 onProgress
}
};
// 将 config.data 转为 formData
let formData = new FormData();
Object.keys(config.data).forEach(key => {
const value = config.data[key];
formData.append(key, value);
});
config.data = formData; // 最后将 formData 重新赋值给 data,接口调用参数
// 打开 “上传中” 提示
if(this.messageInstance == null){
this.messageInstance = this.$message({
type: 'info',
message: '正在上传中,请稍后......,上传过程中,请勿关闭弹框或浏览器!',
duration: 0
})
}
// 调用接口-----文件上传
this.$axios.upload(config).then(result => {
if (result.code === 0) {
info.onSuccess(result); // 解决一直 loading 情况,调用 onSuccess
} else {
info.onError(); // 调用 onError
}
}).catch(err =>{
if(err.message == 'Operation canceled by the user'){
this.$message.warning('用户取消了上传操作')
}else{
info.onError(); // 调用 onError
}
});
},
/**
* 文件自定义分块上传
* @param {Object} info
*/
uploadCustomRequestBlock(info){
const CancelToken = axios.CancelToken; // 获取 axios 取消请求对象
let MAX_FILE_SIZE = 5 * 1024 * 1024; // 设置分割大小为(5242880 B)==(5M)
let index = 0; // 分割数
let file = info.file; // 获取文件
let fileSize = file.size; // 获取文件大小
let start = 0; // 文件切割起始位置
let end = 0; // 文件切割结束位置
let uuid = this.$uuid.v1(); // 创建uuid
let cutNum = Math.ceil(file.size / MAX_FILE_SIZE); // 文件分割的个数
// 文件上传进度回调函数
let handleUploadProgress = (progressEvent) => {
// 这里的上传百分比是切割块的百分比,所以要结合整个文件的大小算上传百分比
let percent = parseInt((progressEvent.loaded + start) / fileSize * 100);
// 如果格式化之后超过了 100% 之后,直接变成 100%
if (percent > 100) {
percent = 100;
}
let progress = { percent: percent };
info.onProgress(progress); // 调用 onProgress
}
// 上传成功的回调函数
let handleUploadSuccess = (res) => {
if (res.code == 0) {
// 调用全部分块合并接口
this.$axios.uploadBlockMerge({
data:{
projectId: '0', //项目主键
hash: uuid, //唯一值
filename: file.name, //文件
dataType: '3', //1 文档数据 2 空间数据 3 文件
total: index + 1, // 文件总数
}
}).then(message => {
if (message.code == 0) {
info.onSuccess(message); // 解决一直 loading 情况,调用onSuccess
} else {
info.onError(); // 调用 onError
}
})
} else {
info.onError(); // 调用 onError
}
}
// 上传失败的回调函数
let handleUploadError = (err) => {
if(err.message == 'Operation canceled by the user'){
this.$message.warning('用户取消了上传操作')
}else{
info.onError(); // 调用 onError
}
}
// 文件上传切割方法
let uploadFileByBlock = () => {
if (start + MAX_FILE_SIZE >= fileSize) { // 判断切割的文件是否大于总文件大小
end = fileSize; // 如果大于总文件大小,将总文件大小直接作为一块
}else{
end = start + MAX_FILE_SIZE; // 如果小于总文件大小,
}
const fileBlock = file.slice(start, end); // 文件切割
const formData = new FormData(); // 创建 FormData 对象
// // 数据内容
// formData.append('file', fileBlock);
// // 第几块数据
// formData.append('index', index);
// // 上传数据大小
// formData.append('size', end - start);
// 请求配置
let config = {
data: {
file: fileBlock, // 上传的文件
hash: uuid, // 生成的 uuid
chunkNumber: index, // 第几块
dataType: '3', // 1 文档数据 2 空间数据 3 文件
},
timeout: 0,
headers: {
'Content-Type': 'multipart/form-data',
},
cancelToken: new CancelToken(c => {
// this.cancelToken = c; // 单文件上传
this.cancelToken.push({ // 多文件上传
uid: info.file.uid,
cancelToken: c
})
}),
// 上传处理进度事件
onUploadProgress: handleUploadProgress
};
Object.keys(config.data).forEach(key => {
const value = config.data[key];
formData.append(key, value);
});
config.data = formData; // 最后将 formData 重新赋值给 data,接口调用参数
// 打开 “上传中” 提示
if(this.messageInstance == null){
this.messageInstance = this.$message({
type: 'info',
message: '正在上传中,请稍后......,上传过程中,请勿关闭弹框或浏览器!',
duration: 0
})
}
// 调用接口-----文件分割上传
this.$axios.uploadBlock(config).then(res => {
// 判断当前分割是否是最后一块
if (start + MAX_FILE_SIZE >= fileSize) {
handleUploadSuccess(res); // 分割全部上传
} else {
start += MAX_FILE_SIZE;
index += 1;
uploadFileByBlock();
}
}).catch(err =>{
handleUploadError(err);
});
}
uploadFileByBlock(); // 调用文件上传切割方法
},
/**
* 文件上传时的钩子
* @param {Object} event
* @param {Object} file
* @param {Object} fileList
*/
handleProgress(event, file, fileList){
console.log(event)
},
/**
* 文件上传成功时的钩子
* @param {Object} response
* @param {Object} file
* @param {Object} fileList
*/
handleSuccess(response, file, fileList){
this.$message.success(`文件 ${ file.name } 上传成功!`);
// 删除 axios 取消请求对象
for(let i = 0; i < this.cancelToken.length; i++){
if(this.cancelToken[i].uid == file.uid){
this.cancelToken.splice(i, 1);
break;
}
}
this.fileList = fileList;
},
/**
* 文件上传失败时的钩子
* @param {Object} err
* @param {Object} file
* @param {Object} fileList
*/
handleError(err, file, fileList){
this.$message.error(`文件 ${ file.name } 上传失败!`);
// 删除 axios 取消请求对象
for(let i = 0; i < this.cancelToken.length; i++){
if(this.cancelToken[i].uid == file.uid){
this.cancelToken.splice(i, 1);
break;
}
}
},
/**
* 点击文件列表中已上传的文件时的钩子
* @param {Object} file
*/
handlePreview(file){
console.log(file)
},
/**
* 删除文件之前的钩子,参数为上传的文件和文件列表,若返回 false 或者返回 Promise 且被 reject,则停止删除
* @param {Object} file
* @param {Object} fileList
*/
handleBeforeRemove(file, fileList){
if (file && file.status === 'success') { // 阻止 before-upload 之后触发 before-remove 以及 on-remove 方法
return this.$msgbox({
title: '提示',
// message: `确定移除 ${ file.name }?`,
message: '此操作将永久删除此文件,是否继续', // MessageBox 消息正文内容
customClass: 'deleteAppMsgbox',
showConfirmButton: true, // 是否显示确定按钮
showCancelButton: true, // 是否显示取消按钮
confirmButtonText: '确定', // 确定按钮的文本内容
cancelButtonText: '取消', // 取消按钮的文本内容
confirmButtonClass: 'sureBtn', // 确定按钮的自定义类名
cancelButtonClass: 'cancelBtn', // 取消按钮的自定义类名
closeOnClickModal: false, // 是否可通过点击遮罩关闭 MessageBox
closeOnPressEscape: false, // 是否可通过按下 ESC 键关闭 MessageBox
type: 'warning'
}).then(() => {
// this.$emit('handleBeforeRemove', file, fileList, val => {
// if(!val){
// reject();
// }
// });
this.$axios.questionBank.deleteProblemFile({
params: {
filePath: file.response ? file.response.data.data.filePath[0] : file.url
}
}).then(res => {
if(res.data.data == '删除成功'){
this.$message.success('删除成功');
}else{
this.$message.error('删除失败');
reject();
}
}).catch(err => {
this.$message.error('删除失败');
reject();
})
}).catch(() => {
this.$message.info('已取消删除');
reject();
})
}else if(file && file.status === 'uploading'){ // 当文件状态为 “上传中” ,则取消请求
for(let i = 0; i < this.cancelToken.length; i++){
if(this.cancelToken[i].uid == file.uid){
// 取消上传请求(message 参数是可选的),建议传递 message 字符串,这样请求在被取消后会被 catch 捕获,并提示给用户
this.cancelToken[i].cancelToken('Operation canceled by the user');
this.cancelToken.splice(i, 1); // 删除 axios 取消请求对象
break;
}
}
}
},
/**
* 文件列表移除文件时的钩子
* @param {Object} file
* @param {Object} fileList
*/
handleRemove(file, fileList){
if (file && (file.status === 'success' || file.status === 'uploading')) { // 阻止 before-upload 之后触发 before-remove 以及 on-remove 方法
// 是否全部文件上传成功
let flag = fileList.every(item => item.status == 'success');
// 全部文件上传成功,关闭 “上传中” 提示
if(flag){
if(this.messageInstance != null){
this.messageInstance.close();
this.messageInstance = null;
}
}
this.fileList = fileList;
}
},
}
}
</script>
注意:如果文件上传后,有提交需求,记得遍历 this.fileList 如果当前对象有 response 代表是上传的,如果没有代表回显的
uploadCustomRequest 参数
![](https://img.haomeiwen.com/i6897582/99f518c7f8596685.png)
uploadingFileInfo.png
已上传的文件
![](https://img.haomeiwen.com/i6897582/9565c56f7e505659.png)
upLoadedFileInfo.png
网友评论