最近项目需求使用到大附件上传功能,图片最多5M,视频100M,结合查询到的资料决定使用webuploader上传组件,项目展示效果如下:
流程图:
1.上传前判断文件格式,项目只支持.png,.jpeg,.jpg,.mp4的视频和图片
2.图片最多5M,视频100M
3.图片可多选上传,视频每次上传一个
4.上传前获取uuid、文件名称和总文件md5判断是否重名和是否已上传过
5.已上传过时跳过上传直接进度显示99,并执行保存缩略图这一步
6.上传时显示上传进度,默认5M分片,大小超出自动分片,获取每个分片的md5
7.上传完成后执行合并操作,将所有分片md5和uuid提交与后端
8.后端验证分片个数和所有分片md5个数,合并后的文件md5和总文件md5,返回保存的附件地址url
9.获取url对应的缩略图并执行保存缩略图
template
<template>
<el-row class="webUploader">
<el-col :span="24" class="mb20" :style="{ display: 'flex' }">
<div class="upload-area" :id="'drag'" v-show="is_upload">
<div class="upload" ref="selectFile" :id="'up_single'" >
<div class="upload-icon"><i class="el-icon-upload"></i></div>
<div class="upload-desc">将文件拖到此处,或<em>点击上传</em></div>
<div class="upload-tip">支持上传jpg/png/jpeg/mp4格式文件,图片文件不能超过5M,视频文件不能超过100M。</div>
</div>
</div>
<div class="upload-area" v-show="!is_upload">
<div class="upload" >
视频素材上传中请稍后......
</div>
</div>
<el-dialog
title="提示"
:visible.sync="dialogVisible"
width="450px"
:show-close="false"
:before-close="handleClose">
<span class="model-title" slot="title">
<span class="title">上传失败</span>
<span class="close" @click="dialogVisible = false"><i class="el-icon-close"></i></span>
</span>
<div class="model">
<div class="text">素材不符合上传要求!</div>
<div class="text">支持上传jpg/png/jpeg/mp4格式文件,图片文件不能超过5M,视频文件不能超过100M。</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false" size="small">取 消</el-button>
<el-button type="primary" @click="dialogVisible = false" size="small">确 定</el-button>
</span>
</el-dialog>
<div class="upload-card" v-if="uploadLoading">
<div class="title">素材上传进度</div>
<div class="card-wrap">
<div class="card-item" v-for="(item,index) in value" :key="index">
<div class="file-name">
<i :icon="item.type" class="uploader-file-icon">
</i>
<div class="name"> {{item.fileName}}</div>
</div>
<div class="fileProgress">
<div class="bar-outer">
<div class="bar-inner" :style="{width:item.fileProgress+'%'}"></div>
</div>
{{ item.fileProgress+'%' }}
</div>
</div>
</div>
</div>
</el-col>
</el-row>
</template>
script
<script>
//引入webuploader
import WebUploader from "webuploader";
import "webuploader/dist/webuploader.css";
import api from "@/http/api.js";
import BMF from 'browser-md5-file';
var chunkObj = {}; //用来记录文件的状态、上传中断的位置
var md5Obj = {};//记录文件分片后所有分片的md5
var GUID = WebUploader.Base.guid();//一个GUID
export default {
name: "uploaderList",
components: {},
props: {
accept: {
type: [String, Object],
default: null
},
// 上传地址
url: {
type: String,
default: "/mtv_back/api/mtv-backend/sysMaterial/fileUpload",///mtv_back/api/mtv-backend/operate/upLoadFile
},
// 上传最大数量 默认为100
fileNumLimit: {
type: Number,
default: 100
},
// 大小限制 默认2M
fileSingleSizeLimit: {
type: Number,
default: 5120000
},
fileType: {
type: String,
default: "knowledge"
},
// 上传时传给后端的参数,一般为token,key等
formData: {
type: Object,
default: () => {
return { uuid: null, code: 9,md5:null };
}
},
// 生成formData中文件的key,下面只是个例子,具体哪种形式和后端商议
keyGenerator: {
type: Function,
default: () => `${new Date().getTime()}`
},
multiple: {
type: Boolean,
default: true
},
// 上传按钮ID
uploadButton: {
type: String,
default: ""
},
value: {
type: Array,
default: () => []
},
disabled: {
type: Boolean,
default: () => false
},
groupType:{
type: String,
default: ""
},
},
data() {
return {
uploader: null,
dialogVisible:false,
collapse:false,
panelShow:true,
uploadLoading:false,
Filesize:0,
file_type:'',//保存切换目录之前的type
is_upload:true,//是否能继续上传,视频每次只能1个,图片可以多个
fileObj:{//上传的文件对象
}
};
},
watch: {
disabled(newVal) {
if (newVal) {
this.uploader.destroy();
return false;
}
this.initWebUpload();
},
groupType(newVal){
this.groupType = newVal;
}
},
mounted() {
if (!this.disabled) {
this.$nextTick(()=>{
console.log('初始化');
this.uploader = this.initWebUpload();
})
}
},
methods: {
handleClose(){
this.dialogVisible=false;
},
initWebUpload() {
console.log('初始化2');
// if (this.uploader) {
// this.uploader.destroy();
// }
let _this = this;
WebUploader.Uploader.register({
"before-send-file":"beforeSendFile",
"before-send": "beforeSend"
}, {
"beforeSendFile": function (file) {
// var deferred = WebUploader.Deferred();
// this.$axios.post({
// url: "/PublicInfoManage/ResourceFile/isCheckFiles",
// data: {
// seq: seq,
// fileMd5: $.md5(file.name + file.size + file.ext),
// fileName:file.name
// },
// dataType: "json",
// success: function (data) {
// console.log(data);
// chunkObj = data;
// chunkObj.type = data.type;
// chunkObj.chunk == data.chunk;
// if (data.type == 0) {
// deferred.reject();
// //$("#" + file.id).find(".state").text("文件已上传");
// } else if (data.type == 1) {
// if (data.chunk) {
// deferred.resolve();
// }
// } else {
// deferred.resolve();
// }
// },
// error: function () {
// deferred.resolve();
// }
// })
// deferred.resolve();
// return deferred.promise();
},
"beforeSend": function (block) {//分块对象
console.log('分块11',block);
let _this = this;
console.log('分块11',_this);
if(_this.options.chunked){
var deferred = WebUploader.Deferred();
var curChunk = block.chunk;//第几个分片
var totalChunk = block.chunks;//总共几个分片
(new WebUploader.Uploader()).md5File(block.file, block.start, block.end)
.progress(function(percentage) {
console.log("正在读取文件");
})
.then(function(val) {
//block.md5 = val;
console.log("分块的md5",val);
console.log('当前的md5',_this.options.formData.md5);
// _this.options.formData.chunk = block.chunk;
// _this.options.formData.fileSize = block.file.size;//分片大小
// if(block.file.name.indexOf('.jpg')!=-1 || block.file.name.indexOf('.jpeg')!=-1){
// }else{
// }
if(md5Obj[block.file.name]){
md5Obj[block.file.name].md5List[block.chunk] = val;
}else{
md5Obj[block.file.name] = {
md5List:[],
}
md5Obj[block.file.name].md5List[block.chunk] = val;
}
//deferred.resolve();
if(chunkObj[block.file.name] && chunkObj[block.file.name].lackList){//如果重传只传失败的
if(chunkObj[block.file.name].lackList.indexOf(val)!=-1){
//_this.options.formData.md5 = val;
_this.options.formData.chunk = block.chunk;
_this.options.formData.fileSize = block.file.size;//分片大小
deferred.resolve();
}else{
deferred.reject();//跳过此分块
}
}else{//
//_this.options.formData.md5 = val;
_this.options.formData.chunk = block.chunk;
_this.options.formData.fileSize = block.file.size;//分片大小
deferred.resolve();
}
})
return deferred.promise();
}
}
});
const uploader = (this.uploader = WebUploader.create({
auto: true, // 选完文件后,是否自动上传
server: this.url, // 文件接收服务端
// swf: '/static/lib/webuploader/Uploader.swf', // swf文件路径
dnd:`#drag`,//拖拽容器,不指定则不开启
pick: { id: `#up_single`, multiple: false },
// pick: {
// //pick: { id: "#up_single", multiple: false },
// id: this.$refs.selectFile.$el, // 选择文件的按钮
// multiple: this.multiple // 是否多文件上传 默认false
// },
//accept: this.getAccept(this.accept), // 允许选择文件格式。
threads: 3,
fileNumLimit: this.fileNumLimit, // 限制上传个数
//fileSingleSizeLimit: this.fileSingleSizeLimit, // 限制单个上传图片的大小
formData: this.formData, // 上传所需参数
chunked: true, //分片上传
chunkRetry:3,//允许重传3次
//chunkSize: 2048000, //分片大小5120000,20M
duplicate: true, // 重复上传
timeout: 0, //超时时间不设置超时限制
compress: false,//不启用压缩
resize: false,//尺寸不改变
//上传文件格式
// accept: {
// extensions: "doc,docx,pdf,txt,xls,xlsx,jpg,png,mp4,avi",
// mimeTypes: ".doc,.docx,.pdf,.txt,.xls,.xlsx,.jpg,.png,.mp4,.avi",
// },
// accept: {
// extensions: "zip,rar,ios,7z",
// mimeTypes: ".zip,.rar,.ios,.7z,",
// },
accept: {
extensions: "jpg,png,jpeg,mp4",
mimeTypes: ".jpg,.png,.jpeg,.mp4",
},
}));
const fun = [
"beforeFileQueued",
"fileQueued",
"uploadStart",
"uploadProgress",
"uploadSuccess",
"error",
"uploadError",
"uploadBeforeSend"
];
for (const item of fun) {
uploader.on(item, this[item]);
}
return uploader;
},
beforeFileQueued(file){
this.file_type = this.groupType;
this.Filesize = file.size
console.log('filestart',file);
console.log('uploader1',this.uploader);
if(!this.coverString('.png',file.name) && !this.coverString('.jpg',file.name) && !this.coverString('.jpeg',file.name) && !this.coverString('.mp4',file.name)){
this.$message.error('文件格式不正确,请选择支持的图片或文件格式上传');
if (file) {
// 取消并中断文件上传
this.uploader.cancelFile(file);
// 在队列中移除文件
this.uploader.removeFile(file, true);
//this.value = [];//上传前清空上传记录
this.uploadLoading = false;
return;
}
}
if(this.coverString('.jpg',file.name) || this.coverString('.png',file.name) || this.coverString('.jpeg',file.name)){//图片
if(file.size>(5*1024*1024)){//20M
this.dialogVisible = true;
this.$message.error('素材过大,请注意上传素材的大小不要超过规定限制');
if (file) {
// 取消并中断文件上传
this.uploader.cancelFile(file);
// 在队列中移除文件
this.uploader.removeFile(file, true);
//this.value = [];//上传前清空上传记录
this.uploadLoading = false;
return;
}
}
}
if(this.coverString('.mp4',file.name)){//视频
if(this.value.length>0){
this.uploader.cancelFile(file);
// 在队列中移除文件
this.uploader.removeFile(file, true);
this.$message.error('请等待图片上传完成后再上传视频');
return;
}
if(file.size>(100*1024*1024)){//20M
this.dialogVisible = true;
this.$message.error('视频文件不能超过100M');
// 取消并中断文件上传
this.uploader.cancelFile(file);
// 在队列中移除文件
this.uploader.removeFile(file, true);
//this.value = [];//上传前清空上传记录
this.uploadLoading = false;
return;
if (file) {
}
}
}
//this.value = [];//上传前清空上传记录
console.log('this.uploader',this.uploader);
// //获取文件MD5
let _this = this;
// const bmf = new BMF();
// bmf.md5(
// file,
// (err, md5) => {
// console.log('err:', err);
// console.log('md5 string:', md5); // 97027eb624f85892c69c4bcec8ab0f11
// //data.md5 = val;
// },
// progress => {
// console.log('progress number:', progress);
// },
// );
this.uploader.removeFile(file, true);
let md5 = {};
(new WebUploader.Uploader()).md5File(file, 0, file.size)
.progress(function(percentage) {
console.log("正在读取文件");
})
.then(function(val) {
// this.uploader.md5File( file )// 及时显示进度
// .progress(function(percentage) {
// console.log('读取文件:'+parseInt(percentage*100)+"%");
// })
// // 完成
// .then(function(val) {
md5[file.id] = val;
console.log('md5:',val);
let params = {
fileName:file.name,
uuid:'',
chunks:file.size>5242880?Math.ceil(file.size/5242880):1,
fileSize:file.size,
md5:val
};
params.uuid = _this.keyGenerator();
//是否允许上传
_this.$axios.post(api + `/mtv-backend/sysMaterial/filePreCheck`,params)
.then((res) => {
console.log('res',res.data.data);
if(res.data.data.allow){
console.log('允许上传');
_this.uploader.upload(file);
// if(file.name.indexOf('.jpg')!=-1 || file.name.indexOf('.jpeg')!=-1){//jpg图片直接获取
// if(md5Obj[file.name]){
// md5Obj[file.name].md5List[0] = val;
// }else{
// md5Obj[file.name] = {
// md5List:[],
// }
// md5Obj[file.name].md5List[0] = val;
// }
// }
_this.fileObj[file.name] = {//添加附件的md5记录
uuid:params.uuid,
fileMd5List:[],
count:1,
}
if(chunkObj[file.name]){
delete chunkObj[file.name]
}
const { name, size, id } = file;
const obj = {
id,
fileName: name,
fileSize: WebUploader.Base.formatSize(size),
fileProgress: 0,
fileStatus: "待上传",
file,
type:'',
groupType:_this.file_type
};
let text = '.doc,.docx,.pdf,.txt,.xls,.xlsx';
let image = '.jpg,.png,.jpeg';
let video = '.mp4';
let audio = '.avi'
if(name.indexOf('.zip')!=-1){
obj.type='zip';
}else{
text.split(',').forEach(item=>{
if(_this.coverString(item,file.name)){
obj.type='text';
}
})
image.split(',').forEach(item=>{
if(_this.coverString(item,file.name)){
obj.type='image';
}
})
video.split(',').forEach(item=>{
if(_this.coverString(item,file.name)){
obj.type='video';
}
})
audio.split(',').forEach(item=>{
if(_this.coverString(item,file.name)){
obj.type='audio';
}
})
}
//doc,docx,pdf,txt,xls,xlsx,jpg,png,mp4,avi
_this.value.push(obj);
_this.uploadLoading = true;
if(obj.type=='video'){
_this.is_upload=false;
}else{
_this.is_upload = true;
}
}else{
if(res.data.data.materialUrl && res.data.data.materialUrl!=''){//直接返回url保存
const { name, size, id } = file;
const obj = {
id,
fileName: name,
fileSize: WebUploader.Base.formatSize(size),
fileProgress: 99,
fileStatus: "待上传",
file,
type:'',
groupType:_this.file_type
};
let text = '.doc,.docx,.pdf,.txt,.xls,.xlsx';
let image = '.jpg,.png';
let video = '.mp4';
let audio = '.avi'
if(name.indexOf('.zip')!=-1){
obj.type='zip';
}else{
text.split(',').forEach(item=>{
if(_this.coverString(item,file.name)){
obj.type='text';
}
})
image.split(',').forEach(item=>{
if(_this.coverString(item,file.name)){
obj.type='image';
}
})
video.split(',').forEach(item=>{
if(_this.coverString(item,file.name)){
obj.type='video';
}
})
audio.split(',').forEach(item=>{
if(_this.coverString(item,file.name)){
obj.type='audio';
}
})
}
//doc,docx,pdf,txt,xls,xlsx,jpg,png,mp4,avi
_this.value.push(obj);
_this.uploadLoading = true;
const params = {
url:res.data.data.materialUrl,
name:file.name,
materialSize:file.size,
file:null,
groupType:_this.file_type
};
_this.findVideoPoster(params);//获取视频第一帧并上传
}else{
_this.$message({
message: '名称不可重复',
type: 'error'
});
_this.panelShow = false;
//this.uploadLoading = false;
//this.value = [];//上传前清空上传记录
_this.uploader.cancelFile(file);
// 在队列中移除文件
_this.uploader.removeFile(file, true);
}
}
})
});
// const { name, size, id } = file;
// const obj = {
// id,
// fileName: name,
// fileSize: WebUploader.Base.formatSize(size),
// fileProgress: 0,
// fileStatus: "待上传",
// file,
// type:'',
// groupType:_this.file_type
// };
// let text = '.doc,.docx,.pdf,.txt,.xls,.xlsx';
// let image = '.jpg,.png';
// let video = '.mp4';
// let audio = '.avi'
// if(name.indexOf('.zip')!=-1){
// obj.type='zip';
// }else{
// text.split(',').forEach(item=>{
// if(name.indexOf(item)!=-1){
// obj.type='text';
// }
// })
// image.split(',').forEach(item=>{
// if(name.indexOf(item)!=-1){
// obj.type='image';
// }
// })
// video.split(',').forEach(item=>{
// if(name.indexOf(item)!=-1){
// obj.type='video';
// }
// })
// audio.split(',').forEach(item=>{
// if(name.indexOf(item)!=-1){
// obj.type='audio';
// }
// })
// }
// //doc,docx,pdf,txt,xls,xlsx,jpg,png,mp4,avi
// _this.value.push(obj);
// _this.uploadLoading = true;
// if(obj.type=='video'){
// _this.is_upload=false;
// }else{
// _this.is_upload = true;
// }
},
uploadBeforeSend(block,data){
console.log('block',block);
var file = block.file;
if(md5Obj[file.name]){
data.md5 = md5Obj[file.name].md5List[block.chunk];
}
console.log("当前文件id", file.id);
console.log("当前文件名", file.name);
// 上传时添加自定义参数
console.log('获取当前块上传参数',data);
if(this.fileObj[file.name]){
data.uuid = this.fileObj[file.name].uuid;
if(this.fileObj[file.name].fileMd5List.indexOf(data.md5)==-1){
this.fileObj[file.name].fileMd5List.push(data.md5);
}
}else{
this.fileObj[file.name] = {
uuid:data.uuid,
fileMd5List:[],
count:1
}
this.fileObj[file.name].fileMd5List.push(data.md5);
}
//this.uploader.removeFile(file, true);
//let _this = this;
// this.uploader.md5File( file )// 及时显示进度
// .progress(function(percentage) {
// console.log('读取文件:'+parseInt(percentage*100)+"%");
// })
// // 完成
// .then(function(val) {
// data.md5 = val;
// _this.uploader.upload(file);
// })
},
// 当有文件被添加进队列的时候,添加到页面预览
fileQueued(file) {
//this.value = [];//上传前清空上传记录
},
//获取视频第一帧做完封面
findVideoPoster(file) {
let self = this
return new Promise(function(resolve) {
console.log('获取第一帧');
try{
let base64URL = ''
let video = null;
let eventName = '';
if(self.coverString('.png',file.name) || self.coverString('.jpg',file.name) || self.coverString('.jpeg',file.name)){
//video = document.createElement('image');
video = new Image();
eventName = 'load';
video.setAttribute('crossOrigin', 'anonymous') //处理跨域
console.log('获取第一帧001');
video.onload = (()=>{
console.log('获取第一帧003');
let canvas = document.createElement('canvas')
//let canvas = document.getElementById('myCanvas');
//使用视频的宽高作为canvas、预览图的宽高
let width = 200;
let height = 200;
canvas.width = width
canvas.height = height
canvas.getContext('2d').drawImage(video, 0, 0, width, height) //绘制canvas
base64URL = canvas.toDataURL('image/jpeg') //转换为base64,图片格式默认为png,这里修改为jpeg
let fileName = String(new Date().getTime()) + parseInt(Math.random() * 100000) + '.jpeg'
//const imgfile = self.data64toFile(base64URL)
const imgfile = self.dataURLToFile(base64URL,fileName);
file.file = imgfile;
console.log('获取第一帧004');
self.postInfo(file);
})
video.src = file.url;
console.log('video',video);
}else{
video = document.createElement('video');
eventName = 'loadeddata';
video.setAttribute('crossOrigin', 'anonymous') //处理跨域
video.setAttribute('src', file.url);
video.currentTime = 1;
video.addEventListener(eventName, function() {
let canvas = document.createElement('canvas')
//let canvas = document.getElementById('myCanvas');
//使用视频的宽高作为canvas、预览图的宽高
let width = 200;
let height = 200;
canvas.width = width
canvas.height = height
canvas.getContext('2d').drawImage(video, 0, 0, width, height) //绘制canvas
base64URL = canvas.toDataURL('image/jpeg') //转换为base64,图片格式默认为png,这里修改为jpeg
let fileName = String(new Date().getTime()) + parseInt(Math.random() * 100000) + '.jpeg'
//const imgfile = self.data64toFile(base64URL)
const imgfile = self.dataURLToFile(base64URL,fileName);
file.file = imgfile;
console.log('上传素材',file);
console.log('获取第一帧003');
self.postInfo(file);
})
}
}catch(err){
console.log('err',err);
}
})
},
coverString(subStr,str){//判断字符串中是否存在子字符串,不区分大小写,处理素材大小写
let reg = eval("/"+subStr+"/ig");
return reg.test(str);
},
dataURLToFile(file,filename){
const arr = file.split(',');
const mime = arr[0].match(/:(.*?);/)[1];
const bstr = atob(arr[1]);
let n = bstr.length;
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, {type: mime});
},
data64toFile(base64URL) {
const arr = base64URL.split(',')
const mime = arr[0].match(/:(.*?);/)[1]
const bstr = atob(arr[1])
let n = bstr.length
const u8arr = new Uint8Array(n)
while (n--) {
u8arr[n] = bstr.charCodeAt(n)
}
return new Blob([u8arr], { type: mime })
},
postInfo(file){//上传素材信息
this.currentPage = 1;
const formData = new FormData();
formData.append('materialUrl',file.url);
formData.append('materialType',file.groupType);
formData.append('materialName',file.name);
formData.append('thumbnail',file.file);
formData.append('materialSize',Number(this.Filesize));
this.$axios.post(api + "/mtv-backend/sysMaterial/saveSystemMaterial", formData,
{
headers:{
'Content-Type': 'multipart/form-data',
}
})
.then((res) => {
console.log('res',res);
if(res.data.code==200){
this.$message({
message: file.name+'上传成功',
type: 'success'
});
let idx = -1;
this.value.forEach((item,index)=>{
if(item.fileName==file.name){
idx = index
}
})
console.log(this.value,idx);
if(this.value[idx]){//设置进度100
this.value[idx].fileProgress=100;
}
this.value.splice(idx,1);//删除上传记录
if(this.value.length==0){
this.uploadLoading = false;
}
this.is_upload = true;
delete this.fileObj[file.name];//删除保存的md5记录
delete md5Obj[file.name]
this.$emit("input");
}else{
this.$message({
message: file.name+'上传失败',
type: 'error'
});
let idx = -1;
this.value.forEach((item,index)=>{
if(item.fileName==file.name){
idx = index
}
})
this.value.splice(idx,1);
if(this.value.length==0){
this.uploadLoading = false;
}
this.is_upload = true;
}
})
},
// 在这里可以准备好formData的数据
uploadStart(file) {
console.log('aa',file);
this.uploader.options.formData.uuid = this.fileObj[file.name].uuid;
},
// 文件上传过程中创建进度条实时显示。
uploadProgress(file, percentage) {
const fileObj = this.value.find(obj => file.id == obj.id);
fileObj.fileStatus = "上传中";
//console.log('percentage',percentage);
fileObj.fileProgress = parseInt(percentage * 100);
fileObj.file = file;
if(fileObj.fileProgress==100){
fileObj.fileProgress = 99;
}
//this.$emit("input", this.value);
},
// 文件上传成功
async uploadSuccess(file, res) {
const fileObj = this.value.find(obj => file.id == obj.id);
console.log('上传结果',file,res);
if(res.code==200){
fileObj.fileId = file.id;
fileObj.fileStatus = "上传成功";
//this.$emit("input", this.value);
let form = this.fileObj[file.name];
//发起合并请求
this.$axios.post(api + "/mtv-backend/sysMaterial/fileMerge", form,{ timeout: 120000 })
.then((res) => {
console.log('合并请求结果res',res);
if(res.data.code==200){
if(!res.data.data.lackList){//分块丢失
if(res.data.data.materialUrl=='' || !res.data.data.materialUrl){//合并url出错
this.showError(file);
return;
}
if(chunkObj[file.name]){//重传完成删除记录
delete chunkObj[file.name]
}
const params = {
url:res.data.data.materialUrl,
name:file.name,
materialSize:res.data.data.fileSize,
file:null,
groupType:fileObj.groupType
};
this.findVideoPoster(params);//获取视频第一帧并上传
}else{//重传分块
if(this.fileObj[file.name].count>=3){//重传3次依旧失败则不重传
this.showError(file);
return;
}
chunkObj[file.name] = {//保存出错的分块
lackList:res.data.data.lackList,
}
this.fileObj[file.name].count++;
this.uploader.upload(file);
}
}else{//合并出错
this.showError(file);
}
})
}else{//上传失败
this.showError(file);
}
},
showError(file){
this.$message({
message: file.name+'上传失败',
type: 'error'
});
let idx = -1;
this.value.forEach((item,index)=>{
if(item.name==file.name){
idx = index
}
})
this.value.splice(idx,1);
if(this.value.length==0){
this.uploadLoading = false;
}
this.is_upload = true;
},
error(type) {//报错信息
console.log('error type',type);
this.uploadLoading = false;
let errorMessage = "";
if (type === "F_EXCEED_SIZE") {
errorMessage = `文件大小不能超过${this.fileSingleSizeLimit /
(1024 * 100)}M`;
} else if (type === "Q_EXCEED_NUM_LIMIT") {
errorMessage = "文件上传已达到最大上限数";
} else {
errorMessage = `上传出错!请检查后重新上传!错误代码${type}`;
}
console.error(errorMessage);
},
uploadError(file){
console.log('uploadError',file);
this.$message({
message: file.name+'文件上传失败',
type: 'error'
});
let idx = -1;
this.value.forEach((item,index)=>{
if(item.name==file.name){
idx = index
}
})
this.value.splice(idx,1);
if(this.value.length==0){
this.uploadLoading = false;
}
this.is_upload = true;
},
// 开始
resume(file) {
this.uploader.upload(file);
},
// 暂停
stop(file) {
file.fileStatus = "暂停中";
this.uploader.stop(file);
},
// 移除
async remove(row, idx) {
const { fileId, file } = row;
try {
//if (fileId) await delFileApi(fileId);
this.value.splice(idx, 1);
if (file) {
// 取消并中断文件上传
this.uploader.cancelFile(file);
// 在队列中移除文件
this.uploader.removeFile(file, true);
}
//this.$emit("input", this.value);
} catch (error) {
console.log(error);
}
},
// 预览
imgLook(fileId) {
if (!fileId) {
return false;
}
lookImg(fileId).then(res => {
const sourceImages = [];
sourceImages.push({
thumbnail:
process.env.VUE_APP_BASE_API +
this.profile +
res.data.thumbnailFile,
source:
process.env.VUE_APP_BASE_API + this.profile + res.data.thumbnailFile
});
// console.log(sourceImages);
this.$refs.viewer.show(sourceImages, 0);
});
},
//上传文件格式
getAccept(accept) {
switch (accept) {
case "text":
return {
title: "Texts",
exteensions: "doc,docx,xls,xlsx,ppt,pptx,pdf,txt",
mimeTypes: ".doc,docx,.xls,.xlsx,.ppt,.pptx,.pdf,.txt"
};
case "video":
return {
title: "Videos",
exteensions: "mp4",
mimeTypes: ".mp4"
};
case "image":
return {
title: "Images",
exteensions: "gif,jpg,jpeg,bmp,png,tif",
mimeTypes: ".gif,.jpg,.jpeg,.bmp,.png,.tif"
};
default:
return accept;
}
},
submitFile() {
this.uploader.upload();
},
// cancelFileUpload(){//取消上传,调取接口删除上传文件流
// const { fileId, file } = this.value[0];
// // 取消并中断文件上传
// this.uploader.cancelFile(file);
// // 在队列中移除文件
// this.uploader.removeFile(file, true);
// this.$axios.post(api + "/mtv-backend/sysMaterial/saveSystemMaterial", {fileName:file.name})
// .then((res) => {
// console.log('res',res);
// if(res.data.code==200){
// }else{
// }
// })
// },
//文件大小单位换算
sizeConversion(size){
if (size < 1024) {
size = size.toFixed(2) + "B";
}else if (size < 1048576) {
size = (size / 1024).toFixed(2) + "K";
}else if (size < 1073741824) {
size = (size / 1048576).toFixed(2) + "M";
}else {
size = (size / 1073741824).toFixed(2) + "G";
}
return size;
},
close() {
//this.uploader.destroy();
this.panelShow = false
},
}
};
</script>
style
<style lang="less" scoped>
/deep/ .webuploader-pick{
background-color: #fff;
}
/deep/ .el-dialog{
border-radius: 4px;
}
/deep/ .el-dialog__header{
background-color: whitesmoke;
padding: 15px 20px 15px;
box-shadow: rgb(233 233 233) 0px 1px 1px;
border-radius: 4px 4px 0 0;
}
/deep/ .el-dialog__footer{
box-shadow: rgb(233, 233, 233) 0px 1px 1px 0px inset;
padding: 14px 20px 14px;
}
.upload-area{
background-color: #f9f9f9;
border: 1px dashed #d9d9d9;
border-radius: 6px;
box-sizing: border-box;
width: 500px;
height: 184px;
text-align: center;
cursor: pointer;
overflow: hidden;
margin-bottom: 30px;
display: flex;
align-items: center;
justify-content: center;
&:hover{
border-color: #409EFF;
background-color: #fff;
.upload .upload-icon i{
color: #409EFF;
}
}
.upload{
font-size: 14px;
.upload-icon{
margin-bottom: 20px;
i{
font-size: 67px;
color: #C0C4CC;
}
}
.upload-desc{
color: #606266;
font-size: 14px;
text-align: center;
margin-bottom: 20px;
}
.upload-tip{
color: #999999;
font-size: 14px;
}
}
}
.model-title{
color:#333;
font-weight:bold;
font-size: 13px;
display: flex;
align-items: center;
justify-content: space-between;
.title{
display: flex;
align-items: center;
}
i{
font-size: 20px;
}
.close{
cursor: pointer;
i{
font-weight: 700;
font-size: 20px;
color: #999;
}
}
}
.model{
margin-top: -20px;
.tip{
margin-bottom:10px;
color:#333;
font-weight:bold;
}
.text{
line-height: 28px;
font-size: 14px;
color: #999;
}
}
// .upload-list{
// position: fixed;
// width: 600px;
// bottom: 0px;
// left: 0;
// z-index: 1;
// border-top: 1px solid #d9d9d9;
// background-color: #fff;
// border: 1px solid #e2e2e2;
// border-radius: 7px 7px 0 0;
// box-shadow: 0 0 10px rgb(0 0 0 / 20%);
// .file-title {
// display: flex;
// height: 40px;
// line-height: 40px;
// padding: 0 15px;
// border-bottom: 1px solid #ddd;
// font-size: 14px;
// .operate {
// flex: 1;
// text-align: right;
// i{
// font-size: 18px;
// }
// .el-button {
// display: inline-block;
// line-height: 1;
// white-space: nowrap;
// cursor: pointer;
// background: #FFF;
// border: 1px solid #DCDFE6;
// color: #606266;
// -webkit-appearance: none;
// text-align: center;
// box-sizing: border-box;
// outline: 0;
// margin: 0;
// transition: .1s;
// font-weight: 500;
// padding: 12px 20px;
// font-size: 14px;
// border-radius: 4px;
// }
// .el-button--text {
// border-color: transparent;
// color: #409EFF;
// background: 0 0;
// padding-left: 0;
// padding-right: 0;
// }
// }
// }
// &.collapse {
// .file-title {
// background-color: #e7ecf2;
// }
// .file-list {
// height: 0;
// }
// }
// }
.uploader-file-name{
.uploader-file-icon[icon=zip]{
background: url(../../assets/images/zip.png);
}
.uploader-file-icon[icon=text]{
background: url(../../assets/images/text-icon.png);
}
.uploader-file-icon[icon=image]{
background: url(../../assets/images/image-icon.png);
}
.uploader-file-icon[icon=video]{
background: url(../../assets/images/video-icon.png);
}
.uploader-file-icon[icon=audio]{
background: url(../../assets/images/audio-icon.png);
}
display: flex;
.name{
flex: 1;
word-break: break-all;
}
.uploader-file-icon {
width: 24px;
height: 24px;
display: inline-block;
vertical-align: top;
// margin-top: 13px;
// margin-right: 8px;
}
}
.upload-card{
padding: 10px;
border-radius: 4px;
background-color: #fff;
position: fixed;
right: 60px;
top: 60px;
z-index: 99;
box-shadow: 0 0 10px rgb(0 0 0 / 20%);
color: #666;
width: 300px;
.title{
font-size: 14px;
font-weight: bold;
margin-bottom: 16px;
}
.card-wrap{
.card-item{
.file-name{
font-size: 12px;
display: flex;
.name{
flex: 1;
word-break: break-all;
}
i{
width: 14px;
height: 14px;
margin-right: 4px;
}
.uploader-file-icon[icon=zip]{
background: url(../../assets/images/zip.png);
background-size: contain;
}
.uploader-file-icon[icon=text]{
background: url(../../assets/images/text-icon.png);
background-size: contain;
}
.uploader-file-icon[icon=image]{
background: url(../../assets/images/image-icon.png);
background-size: contain;
}
.uploader-file-icon[icon=video]{
background: url(../../assets/images/video-icon.png);
background-size: contain;
}
.uploader-file-icon[icon=audio]{
background: url(../../assets/images/audio-icon.png);
background-size: contain;
}
}
.fileProgress{
font-size: 12px;
display: flex;
align-items: center;
.bar-outer{
display: flex;
align-items: center;
border-radius: 100px;
background-color: #ebeef5;
overflow: hidden;
position: relative;
vertical-align: middle;
margin-right: 4px;
height: 8px;
//width: 200px;
flex: 1;
.bar-inner{
position: absolute;
left: 0;
top: 0;
height: 100%;
background-color: #409eff;
text-align: right;
border-radius: 100px;
line-height: 1;
white-space: nowrap;
transition: width .6s ease;
}
}
}
}
}
}
</style>
重点在于beforeSend获取到分片对象,WebUploader提供一个md5File函数获取文件流的md5,uploadBeforeSend函数自定义每个分片上传的参数,在WebUploader配置中记得加上compress:false,resize: false,否则组件在分片时会默认将分片压缩,导致总文件的md5与合并后的md5不一致
compress: false,//不启用压缩
resize: false,//尺寸不改变
(new WebUploader.Uploader()).md5File(block.file, block.start,
block.end)
.progress(function(percentage) {
console.log(“正在读取文件”);
})
.then(function(val) {
})
参考文档:
http://fex.baidu.com/webuploader/doc/#WebUploader_Uploader_md5File
http://t.zoukankan.com/hackerPJ-p-7563023.html