465 lines
15 KiB
Vue
465 lines
15 KiB
Vue
<template>
|
||
<div>
|
||
<el-upload class="upload-demo" ref="upload" action="#"
|
||
:before-upload="beforeUpload"
|
||
:on-change="onChange"
|
||
:on-remove="handleRemove"
|
||
:multiple="isMultiple"
|
||
:on-exceed="handleExceed"
|
||
:accept="acceptType"
|
||
:limit="limit"
|
||
:file-list="fileList"
|
||
:auto-upload="true"
|
||
:http-request="uploadFile"
|
||
show-file-list>
|
||
<el-button slot="trigger" size="small" type="primary">选取文件</el-button>
|
||
<el-button size="small" type="primary" @click="handleUploadFile">
|
||
上传文件夹
|
||
</el-button>
|
||
<!-- 上传-->
|
||
<input v-show="false" ref="fileRef" id="fileFolder" type="file" @change="handleFolderUpload" webkitdirectory
|
||
multiple="multiple" />
|
||
|
||
<!-- <input v-show="false" type="file" ref="fileRef" webkitdirectory @change="handleFolderUpload">
|
||
<el-button slot="trigger" size="small" type="primary">选取文件夹</el-button>-->
|
||
</el-upload>
|
||
<div slot="tip" class="el-upload__tip">只能上传{{acceptType}}文件</div>
|
||
<div v-show="progressFlag" class="head-img">
|
||
<el-progress :text-inside="true" :stroke-width="14" :percentage="progressPercent" status="success"></el-progress>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import axios from 'axios'
|
||
import {
|
||
getToken
|
||
} from "@/utils/auth";
|
||
import JSZip from 'jszip'
|
||
|
||
export default {
|
||
props: {
|
||
uploadUrl: {
|
||
type: String,
|
||
required: true
|
||
},
|
||
// 是否多选
|
||
isMultiple: {
|
||
type: Boolean,
|
||
default() {
|
||
return false
|
||
}
|
||
},
|
||
// 文件类型
|
||
type: {
|
||
type: Array,
|
||
default() {
|
||
return []
|
||
}
|
||
},
|
||
//上传类型
|
||
acceptType: {
|
||
type: String,
|
||
required: true
|
||
},
|
||
// 上传数量
|
||
limit: {
|
||
type: Number,
|
||
default() {
|
||
return 1
|
||
}
|
||
},
|
||
// 文件
|
||
dataFile: {
|
||
type: [Object, Array, String],
|
||
default() {
|
||
return ''
|
||
}
|
||
},
|
||
},
|
||
data() {
|
||
return {
|
||
fileList: [],
|
||
progress: 0,
|
||
isUploading: false,
|
||
progressFlag: false, //进度条初始值隐藏
|
||
progressPercent: 0, //进度条初始值
|
||
partSize: 5 * 1024 * 1024,
|
||
};
|
||
},
|
||
watch: {
|
||
dataFile: {
|
||
handler(newValue, oldValue) {
|
||
if (newValue) {
|
||
this.fileList = Array.isArray(newValue) ? newValue : [newValue]
|
||
} else {
|
||
this.fileList = []
|
||
}
|
||
},
|
||
immediate: true
|
||
}
|
||
},
|
||
methods: {
|
||
// 上传前
|
||
beforeUpload(file) {
|
||
/* const fileType = file.type.toLowerCase()
|
||
console.info("fileType=======", fileType)
|
||
const typeFlag = this.type.some(item => {
|
||
return fileType.indexOf(item) != -1
|
||
})
|
||
if (!typeFlag) {
|
||
this.$message.error('上传类型有误,请重新上传')
|
||
return false
|
||
}*/
|
||
},
|
||
//文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
|
||
onChange(file, fileList) {
|
||
this.fileList = fileList;
|
||
},
|
||
handleRemove(file, fileList) {
|
||
this.fileList = fileList;
|
||
},
|
||
handleExceed(files, fileList) {
|
||
this.$message.warning(
|
||
"最多之能上传"+ this.limit +"个附件"
|
||
);
|
||
},
|
||
async uploadFile({ data, file }) {
|
||
let self = this
|
||
//初始化参数
|
||
if (file.size < this.partSize) {
|
||
let formData = new FormData()
|
||
formData.append("file", file)
|
||
self.progressFlag = true;
|
||
axios({
|
||
url: self.uploadUrl,
|
||
method: 'post',
|
||
data: formData,
|
||
headers: {
|
||
'Authorization': 'Bearer ' + getToken(),
|
||
'Content-Type': 'multipart/form-data'
|
||
},
|
||
onUploadProgress: progressEvent => {
|
||
// progressEvent.loaded:已上传文件大小
|
||
// progressEvent.total:被上传文件的总大小
|
||
//进度条
|
||
self.progressPercent = ((progressEvent.loaded / progressEvent.total) * 100) | 0;
|
||
}
|
||
}).then(res => {
|
||
setTimeout(() => {
|
||
if (res.data.code == 200 && self.progressPercent === 100) {
|
||
setTimeout(function () {
|
||
self.$message({
|
||
message: '上传成功!',
|
||
type: 'success',
|
||
duration: '2000'
|
||
});
|
||
self.progressFlag = false;
|
||
self.progressPercent = 0
|
||
self.handleResult(res,file);
|
||
}, 500);
|
||
} else {
|
||
self.$message({
|
||
message: '上传失败!',
|
||
type: 'error',
|
||
duration: '2000'
|
||
});
|
||
}
|
||
}, 1000);
|
||
|
||
}).catch(error => {
|
||
console.error(error)
|
||
self.progressFlag = false;
|
||
self.progressPercent = 0
|
||
self.$refs.upload.clearFiles();
|
||
self.$message({
|
||
message: '上传失败!',
|
||
type: 'error',
|
||
duration: '2000'
|
||
});
|
||
})
|
||
} else {
|
||
file._shardCount = Math.ceil(file.size / this.partSize) //总片数
|
||
file.uploaded = 0
|
||
let formData = new FormData()
|
||
formData.append('fileName', file.name)
|
||
let self = this
|
||
//大文件上传初始化,返回uploadId initUpload(formData)
|
||
axios({
|
||
url: process.env.VUE_APP_BASE_API + "/common/initUpload",
|
||
method: 'post',
|
||
data: formData,
|
||
headers: {
|
||
'Authorization': 'Bearer ' + getToken(),
|
||
'Content-Type': 'multipart/form-data'
|
||
},
|
||
}).then(function (res) {
|
||
if (res.status == 200) {
|
||
//从第0块开始上传
|
||
file.uploadId = res.data.uploadId
|
||
file.objectKey = res.data
|
||
file.chunkId = 0
|
||
self.uploadByChunk(file)
|
||
} else {
|
||
this.progress--
|
||
self.$message.error(res.msg)
|
||
}
|
||
}).catch(error => {
|
||
self.progressPercent = 0
|
||
self.progressFlag = false;
|
||
console.error(error)
|
||
})
|
||
}
|
||
|
||
},
|
||
//分片上传大文件
|
||
uploadByChunk(file) {
|
||
this._start = file.chunkId * this.partSize
|
||
this._end = Math.min(file.size, this._start + this.partSize)//结束时总大小,和 开始的大小+之前的大小比较
|
||
|
||
let self = this
|
||
let fileData = file.slice(this._start, this._end)
|
||
//获取文件块MD5
|
||
let reader = new FileReader()
|
||
reader.readAsBinaryString(fileData)
|
||
let form1 = new FormData()//new一个form的实例,可以进行键值对的添加,
|
||
form1.append('chunkFile', fileData) //slice方法用于切出文件的一部分
|
||
form1.append('uploadId', file.uploadId)
|
||
form1.append('chunkId', (file.chunkId + 1).toString())
|
||
form1.append('shardCount', file._shardCount.toString()) //是否最后一片
|
||
self.progressFlag = true;
|
||
//上传大文件的Chunk 返回chunk的MD5 uploadChunk(form1)
|
||
axios({
|
||
url: process.env.VUE_APP_BASE_API + "/common/uploadChunk",
|
||
method: 'post',
|
||
data: form1,
|
||
headers: {
|
||
'Authorization': 'Bearer ' + getToken(),
|
||
'Content-Type': 'multipart/form-data'
|
||
},
|
||
onUploadProgress: progressEvent => {
|
||
// progressEvent.loaded:已上传文件大小
|
||
// progressEvent.total:被上传文件的总大小
|
||
//进度条
|
||
self.progressPercent = ((progressEvent.loaded / progressEvent.total) * 100) | 0;
|
||
}
|
||
}).then(response => {
|
||
if (response.status == 200) {
|
||
//判断返回的MD5值是否一致,一致继续传下一块,否则重传本块(这里目前前后端MD5一直不一致,暂时先注释掉,有时间了我再研究一下)
|
||
// self.md5Str[index] === response.msg ||
|
||
if (true) {
|
||
// this.$message.success('第' + (index + 1).toString() + '块文件上传成功')
|
||
file.uploaded++
|
||
self.percentageSend(file)
|
||
//如果没上传完成,继续上传一下块
|
||
file.chunkId++
|
||
if (file.chunkId < file._shardCount) {
|
||
this.uploadByChunk(file)
|
||
}
|
||
} else {
|
||
//不一致,重新传本块
|
||
this.$message.success('第' + (file.chunkId + 1).toString() + '块文件上传不成功,重新上传')
|
||
this.uploadByChunk(file.chunkId)
|
||
}
|
||
} else {
|
||
//出错,跳出循环显示错误
|
||
this.progress--
|
||
this.$message.error(response.msg)
|
||
}
|
||
})
|
||
},
|
||
//接收上传的百分值回调
|
||
percentageSend(file) {
|
||
let self = this
|
||
let perNum = Math.floor((file.uploaded / file._shardCount) * 100)
|
||
this.progressPercent = perNum
|
||
//如果上传完成,合并文件
|
||
if (perNum === 100) {
|
||
let form2 = new FormData()//new一个form的实例,可以进行键值对的添加,
|
||
form2.append('uploadId', file.uploadId)
|
||
form2.append('fileName', file.name)
|
||
// 大文件上传完成后合并
|
||
// 返回文件访问的URL
|
||
axios({
|
||
url: process.env.VUE_APP_BASE_API + "/common/mergeFile",
|
||
method: 'post',
|
||
data: form2,
|
||
headers: {
|
||
'Authorization': 'Bearer ' + getToken(),
|
||
'Content-Type': 'multipart/form-data'
|
||
},
|
||
}).then(res => {
|
||
if (res.status == 200) {
|
||
setTimeout(function () {
|
||
self.$message({
|
||
message: '上传成功!',
|
||
type: 'success',
|
||
duration: '2000'
|
||
});
|
||
self.progressFlag = false;
|
||
self.progressPercent = 0
|
||
self.handleResult(res,file);
|
||
}, 500);
|
||
} else {
|
||
self.$message({
|
||
message: '上传失败!',
|
||
type: 'error',
|
||
duration: '2000'
|
||
});
|
||
}
|
||
})
|
||
}
|
||
},
|
||
resetData() {
|
||
this.progress = 0;
|
||
},
|
||
handleResult(res, file) {
|
||
let self = this
|
||
const data = {
|
||
fileName: file.name,
|
||
fileSize: file.size,
|
||
fileUrl: res.data.url,
|
||
filePath: res.data.filePath,
|
||
fileOldName: res.data.originalFilename,
|
||
fileNewName: res.data.newFileName,
|
||
suffixType: res.data.suffixType
|
||
}
|
||
console.info("data===========", data)
|
||
self.$emit("handleSuccess", data)
|
||
},
|
||
/** 清空文件 **/
|
||
clearFile(){
|
||
this.$refs.upload.clearFiles();
|
||
this.fileList = []
|
||
},
|
||
handleUploadFile() {
|
||
document.getElementById("fileFolder").value = null;
|
||
this.$refs.fileRef.dispatchEvent(new MouseEvent("click"));
|
||
},
|
||
async handleFolderUpload(event) {
|
||
let self = this;
|
||
const files = event.target.files;
|
||
this.fileslength = files.length;
|
||
|
||
// 获取文件夹名称
|
||
let folderName = "";
|
||
if (files.length > 0) {
|
||
let relativePath = files[0].webkitRelativePath;
|
||
folderName = relativePath.split("/")[0];
|
||
}
|
||
|
||
const zip = new JSZip();
|
||
|
||
// 将文件夹及其内容添加到 ZIP 中
|
||
Array.from(files).forEach(file => {
|
||
zip.file(file.webkitRelativePath, file);
|
||
});
|
||
|
||
// 生成 ZIP 文件
|
||
const zipBlob = await zip.generateAsync({ type: "blob" });
|
||
|
||
// 创建 FormData 对象
|
||
/* const formData = new FormData();
|
||
formData.append("file", zipBlob, folderName + ".zip");*/
|
||
let formData = new FormData()
|
||
formData.append('fileName', folderName + ".zip")
|
||
|
||
// 计算 shardCount
|
||
const _shardCount = Math.ceil(zipBlob.size / this.partSize);
|
||
const fileObj = {
|
||
_shardCount,
|
||
size: zipBlob.size,
|
||
file: zipBlob // 将 zipBlob 添加到 fileObj 中
|
||
};
|
||
self.$set(fileObj.file, '_shardCount', _shardCount);
|
||
self.$set(fileObj.file, 'uploaded', 0);
|
||
self.$set(fileObj.file, 'name', folderName + ".zip");
|
||
self.fileList.push(fileObj.file)
|
||
|
||
if (zipBlob.size < this.partSize) {
|
||
let formDataSign = new FormData()
|
||
formDataSign.append("file", zipBlob, folderName + ".zip")
|
||
self.progressFlag = true;
|
||
axios({
|
||
url: self.uploadUrl,
|
||
method: 'post',
|
||
data: formDataSign,
|
||
headers: {
|
||
'Authorization': 'Bearer ' + getToken(),
|
||
'Content-Type': 'multipart/form-data'
|
||
},
|
||
onUploadProgress: progressEvent => {
|
||
// progressEvent.loaded:已上传文件大小
|
||
// progressEvent.total:被上传文件的总大小
|
||
//进度条
|
||
self.progressPercent = ((progressEvent.loaded / progressEvent.total) * 100) | 0;
|
||
}
|
||
}).then(res => {
|
||
setTimeout(() => {
|
||
if (res.data.code == 200 && self.progressPercent === 100) {
|
||
setTimeout(function () {
|
||
self.$message({
|
||
message: '上传成功!',
|
||
type: 'success',
|
||
duration: '2000'
|
||
});
|
||
self.progressFlag = false;
|
||
self.progressPercent = 0
|
||
self.handleResult(res,fileObj.file);
|
||
}, 500);
|
||
} else {
|
||
self.$message({
|
||
message: '上传失败!',
|
||
type: 'error',
|
||
duration: '2000'
|
||
});
|
||
}
|
||
}, 1000);
|
||
|
||
}).catch(error => {
|
||
console.error(error)
|
||
self.progressFlag = false;
|
||
self.progressPercent = 0
|
||
self.$refs.upload.clearFiles();
|
||
self.$message({
|
||
message: '上传失败!',
|
||
type: 'error',
|
||
duration: '2000'
|
||
});
|
||
})
|
||
} else {
|
||
// 初始化上传,获取 uploadId
|
||
axios({
|
||
url: process.env.VUE_APP_BASE_API + "/common/initUpload",
|
||
method: 'post',
|
||
data: formData,
|
||
headers: {
|
||
'Authorization': 'Bearer ' + getToken(),
|
||
'Content-Type': 'multipart/form-data'
|
||
},
|
||
}).then(function (res) {
|
||
if (res.status === 200) {
|
||
self.$set(fileObj.file, 'uploadId', res.data.uploadId);
|
||
self.$set(fileObj.file, 'chunkId', 0);
|
||
self.$set(fileObj.file, 'objectKey', res.data);
|
||
|
||
/*//从第0块开始上传
|
||
fileObj.uploadId = res.data.uploadId;
|
||
fileObj.chunkId = 0;
|
||
fileObj.objectKey = res.data*/
|
||
self.uploadByChunk(fileObj.file);
|
||
} else {
|
||
self.progress--;
|
||
self.$message.error(res.msg);
|
||
}
|
||
}).catch(error => {
|
||
self.progressPercent = 0;
|
||
self.progressFlag = false;
|
||
console.error(error);
|
||
});
|
||
}
|
||
},
|
||
}
|
||
};
|
||
</script>
|