之前做的一个项目中,由于经常上传几百兆的压缩包,导致经常上传失败,所以就找了webuploader插件做了断点续传
断点续传除了需要前端分片,也需要后台去支持,所以做的时候做好对接协调
1、下载webuploader
npm i webuploader
2、导入并挂载到原型上
在main.js中导入,全局注册,挂载到原型上方便调用
import WebUploader from 'webuploader';
Vue.prototype.WebUploader = WebUploader;
import "webuploader/dist/webuploader.css";
挂载到原型上之后就可以通过 this.WebUploader 去调用
之所以还要导入 webuploader.css 文件,是因为这样在初始化时,才会有正常的按钮样式,样式不符合需求也可以通过自己写样式去覆盖掉,否则初始化之后就只是一个type = file的input框,不方便调整样式
3、md5校验
上传一半取消了,再次上传,怎么接着上传?就需要进行md5校验,后台才知道原来这个文件之前已经上传过了,不需要重头上传
另外,md5的校验必须要写在初始化之前
// 断点续传的按钮初始化之前调用
webUploadBeforeInit(chunkSize) {
this.WebUploader.Uploader.register({
"before-send-file": "beforeSendFile",
"before-send": "beforeSend",
}, {
// 时间点1:所有分块进行上传之前调用此函数
beforeSendFile: (file) => {
let deferred = this.WebUploader.Deferred();
let Uploader = new this.WebUploader.Uploader()
Uploader.md5File(file, 0, chunkSize).progress(percentage => {
debugger;
console.log("校验MD5中...")
}).then(md5 => {
file.md5 = md5;
file.uid = this.WebUploader.Base.guid();
// 进行md5判断
this.axios({
url: this.commonUrl + "/FileUploadController/checkFile",
method: "get",
params: {
fileName: file.name,
md5: file.md5,
},
}).then(res => {
let status = res.code;
deferred.resolve();
switch (status) {
case 0: // 表示上传成功或者已经上传过了
// 忽略上传过程,直接标识上传成功;
Uploader.skipFile(file);
file.pass = true;
break;
case 16: // 部分已经上传到服务器了,但是差几个模块。
file.missChunks = res.data;
break;
default:
break;
// 另外我这里,12表示未上传,1表示添加失败,这里不需要做其他处理
}
}).catch((err) => {
deferred.reject();
});
});
return deferred.promise();
},
beforeSend: (block) => {
let deferred = this.WebUploader.Deferred();
// 当前未上传分块
let missChunks = block.file.missChunks;
// 当前分块
let blockChunk = block.chunk;
if (missChunks !== null && missChunks !== undefined &&missChunks !== "") {
let flag = true;
for (let i = 0; i < missChunks.length; i++) {
if (blockChunk === parseInt(missChunks[i])) {
// 存在还未上传的分块
flag = false;
break;
}
}
if (flag) {
deferred.reject();
} else {
deferred.resolve();
}
} else {
deferred.resolve();
}
return deferred.promise();
},
}
);
},
4、封装初始化方法
有关webuploader配置项、事件等,可详细参考 WebUploader API文档
webUploadResume(opt, evt){
if(!(opt && this.isObj(opt))) return console.error("请传类型为对象的配置项opt,pick必传")
// md5校验在初始化之前
this.webUploadBeforeInit(opt.chunkSize || 10485760)
// 初始化
let uploader = this.WebUploader.create({
// 文件上传调的便是此接口
server: this.commonUrl + "/FileUploadController/breakpointUpload",
method: opt.method || "post",
// 这个外部是必传,要选一个dom初始化,无法给默认值。
// 可以直接给ID,如"#abc",可以是对象,如
// {
// id:"#abc", // 虽然名是id,但实际也可以是类名或标签名
// innerHTML:"按钮文字,可不传",
// multiple:true/false 是否多选,
// }
pick: opt.pick, // 制定对那个dom进行初始化
// 指定上传的哪些类型的文件
// {
// title:"文字描述" ,
// extensions:"允许的文件后缀,不带点,多个用逗号分割,比如gif,jpg,jpeg,bmp,png",
// mimeTypes: "多个用逗号分割,如image/*或者.gif,.jpg,.bmp"
// }
accept: opt.accept || null,
resize: opt.resize || false, // 不压缩img
auto: opt.auto || true, // 是否开启自动上传
threads: opt.threads || 1, // 上传并发数。允许同时最大上传进程数
chunked: opt.chunked || true, // 是否分片上传
chunkSize: opt.chunkSize || 10485760, // 分片大小,以B为单位,这里是10M = 10 * 1024 * 1024 B
chunkRetry: opt.chunkRetry || 2, // 如果某个分片由于网络问题出错,允许自动重传多少次
duplicate: opt.duplicate || true, // 重复上传,为了解决上传一个后若上传失败,需再次上传,会无反应
formData: opt.formData || {}, // 文件上传请求的额外参数
fileNumLimit: opt.fileNumLimit || undefined, // 验证文件总数量, 超出则不允许加入队列
fileSizeLimit: opt.fileSizeLimit || undefined, // 验证文件总大小是否超出限制, 超出则不允许加入队列
fileSingleSizeLimit: opt.fileSingleSizeLimit || undefined, // 验证单个文件大小是否超出限制, 超出则不允许加入队列
// 主要看接口是否需要token,不需要的话,下方headers可以去掉
headers:{
token: sessionStorage.tkn
}
})
// 绑定事件
// beforeFileQueued, 当文件被加入队列之前触发
// fileQueued, 当文件被加入队列以后触发
// filesQueued, 当一批文件添加进队列以后触发
// startUpload, 当开始上传流程时触发
// stopUpload, 当开始上传流程暂停时触发
// uploadBeforeSend, 当某个文件的分块在发送前触发,主要用来询问是否要添加附带参数
// uploadProgress, 上传过程中触发,携带上传进度
// uploadSuccess, 当文件上传成功时触发
// uploadError, 当文件上传出错时触发
// uploadComplete, 不管成功或者失败,文件上传完成时触发
// uploadFinished, 当所有文件上传结束时触发
// error, 当validate不通过时,会以派送错误事件的形式通知调用者
// addFail, 当add接口失败后执行(非webuploader插件所带,为自定义)
if(evt && this.isObj(evt)){
for(let k in evt){
if(k == "uploadError" || k == "uploadSuccess"){
uploader.on(k, (file, resp) => {
this.axios({
url:this.commonUrl + "/FileUploadController/add",
method:"post",
data:{
fileName: file.name, // 文件名
suffix: file.ext, // 文件后缀
uploadStatus: k == "uploadSuccess"?1:0,
},
}).then(res => {
evt[k](res,file,resp)
}).catch(err => {
evt.addFail && evt.addFail(file, resp)
})
})
}else if(k != "addFail"){
uploader.on(k, evt[k])
}
}
}else{
console.error("断点续传事件evt未传或传的类型错误")
}
},
5、调用封装的初始化方法进行初始化
<template>
<div id="addFail"></div>
</template>
<script>
data(){
return {}
},
mounted(){
this.webUploaderInit()
},
methods:{
webUploaderInit(){
// 封装的方法也是挂载到原型上的,方便调用
this.$tu.webUploadResume({
pick: "#addFail",
accept: {
title: "rar,zip",
extensions: "rar,zip",
mimeTypes: ".rar,.zip",
},
fileSizeLimit: 1073741824, // 总大小不超过1G
},{
beforeFileQueued: this.beforeFileQueued,
uploadProgress: this.uploadProgress,
uploadSuccess: this.uploadSuccess,
uploadError: this.uploadError,
error: this.error,
addFail: this.addFail,
})
},
beforeFileQueued(file){
// 注意区分beforeFileQueued和uploadBeforeSend
// beforeFileQueued是选择文件时,即将加入要上传的文件队列但还没加入时
// uploadBeforeSend是即将开始调用上传接口,但还没调用
},
uploadProgress(file, percentage){
// percentag,即上传进度
// 一般用法 percentag * 100 + "%" 或者 percentag * width + "px"
},
// 注意我这里uploadSuccess和uploadError跟其他事件注册不太一样,所以接受参数与api文档并不完全一致
uploadSuccess(res,file,resp){……},
uploadError(err,file,resp){……},
error(type){
// 当 type == "Q_EXCEED_SIZE_LIMIT",就意味着选中的文件总大小超过了设置的fileSizeLimit
// 当 type == "Q_EXCEED_NUM_LIMIT",就意味着选中的文件数量超过了设置的fileNumLimit
// 当 type == "Q_TYPE_DENIED",就意味着选中的文件不符合accept中设置的文件格式
// 另外,如果设置了 fileSingleSizeLimit,即使不符合也不会进入到这里
// 因为如果选择了A文件超过,B文件未超,那么会将A文件自动过滤掉,只将B加入到文件队列
},
// 无论uploadSuccess还是uploadError都会调用add接口,那么add也可能会因为某些情况调用失败
// 针对此情况,增加了自定义addFail方法,当add接口调用失败时会执行下面方法
addFail(file, resp){……},
},
</script>
初始化成功之后,页面便能看到这样一个按钮
点击即可上传。若样式不符合需求,可以F12找到相关类名,进行样式覆盖
另外,在覆盖样式时,注意:
- 你看到的“断点续传”的按钮样式是,类名为webuploader-pick的div
- 而你点击时,之所以能打开选择文件的弹窗,并非是点击了这个div,实际是点击了下面的lable标签
- 所以,覆写样式时,注意label标签要和div的位置保持一致,否则就会出现点击但没有任何反应,因为只是点了div,并没有点击到label
参考文档:
WebUploader API文档
vue中大文件上传webuploader前端用法
基于jquery使用webuploader示例
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END