<div id="app">
<el-upload
class="upload-demo"
multiple
limit="5"
:file-list="fileList" // file-list内的变量是只读的,不能随意更改其值,否则会出现不可思议的bug,此属性一般作为数据回显使用
: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: [], // 数组对象字段名分别为 name、url
fileListSrc: [], // 上传成功的文件集合
cancelToken: [], // 每个上传文件的请求的 cancelToken
messageInstance: null, // message提示信息的实例
allFileUploadStatus: true, // 全部文件 是否 全部上传成功
}
},
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 = true;
fileList.map((item, index) => {
if(item.status != 'success'){
flag = false;
}
})
// true: 全部文件上传成功, false: 任何一个文件上传未成功
this.allFileUploadStatus = flag ? true : false;
// 全部文件上传成功,关闭 “上传中” 提示
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({
message: "上传的文件过大!"
});
}
// 如果符合文件类型及大小,则验证文件名是否重名
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 =>{
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) => {
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} 上传成功!`);
// 上传成功的文件插入到 fileListSrc 中
this.fileListSrc.push(file);
// 删除 axios 取消请求对象
for(let i = 0; i < this.cancelToken.length; i++){
if(this.cancelToken[i].uid == file.uid){
this.cancelToken.splice(i--, 1);
}
}
},
/**
* 文件上传失败时的钩子
* @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);
}
}
},
/**
* 点击文件列表中已上传的文件时的钩子
* @param {Object} file
*/
handlePreview(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.$confirm(`确定移除 ${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.$axios.questionBank.deleteProblemFile({
params: {
filePath: file.response.data.data.filePath[0]
}
}).then(res => {
if(res.data.data == '删除成功'){
this.$message.success("删除成功");
}else{
this.$message.error("删除失败");
reject();
}
}).catch(err => {
this.$message.error("删除失败");
reject();
})
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
reject();
})
}else if(file && file.status === 'uploading'){ // 当文件状态为 “上传中” ,则取消请求
for(let i = 0; i < this.cancelToken.length; i++){
if(this.cancelToken[i].uid == file.uid){
this.cancelToken[i].cancelToken(); // 取消上传
this.cancelToken.splice(i--, 1); // 删除 axios 取消请求对象
}
}
}
},
/**
* 文件列表移除文件时的钩子
* @param {Object} file
* @param {Object} fileList
*/
handleRemove(file, fileList){
if (file && file.status === "success") { // 阻止 before-upload 之后触发 before-remove 以及 on-remove 方法
// 是否全部删除
if(fileList.length == 0){
// 更改全部文件状态为:“上传成功”
this.allFileUploadStatus = true;
}
// 是否全部文件上传成功
let flag = true;
fileList.map((item, index) => {
if(item.status != 'success'){
flag = false;
}
})
// 全部文件上传成功,关闭 “上传中” 提示
if(flag){
if(this.messageInstance != null){
this.messageInstance.close();
this.messageInstance = null;
}
}
// 删除文件
for(let i = 0; i < this.fileListSrc.length; i++){
if(this.fileListSrc[i].uid == file.uid){
this.fileListSrc.splice(i--, 1);
}
}
}
},
}
}
</script>
注意:如果文件上传后,有提交需求,记得将 this.fileList 和 this.fileListSrc 合并
uploadCustomRequest 参数
uploadingFileInfo.png
已上传的文件
upLoadedFileInfo.png
网友评论