前言
前面叙述了如何利用vue-simple-uploader实现分片上传、断点续传和秒传;
本篇学习基于WebUploader实现这些功能
使用
1.在项目中引入webuploader
注意事项
1、插件基于jquery,
2、不支持npm导入(通过 import './webuploader'; 的方式引入webuploader,会报''caller', 'callee', and 'arguments' properties may not be accessed on strict mode ...'的错,)
我们在项目index.html中进行静态引入资源
在 官网 上下载
Uploader.swf 和 webuploader.min.js,放到静态static文件下
注意事项
如遇到报错:Uncaught SyntaxError: Unexpected token ‘<‘,
是vue静态资源引入错误:https://blog.csdn.net/weixin_43909743/article/details/118804322
- 封装上传组件upload.vue
封装好的组件upload.vue如下,接口可以根据具体的业务进行扩展。
<template>
<div class="upload">
</div>
</template>
<script>
import { getToken } from "@/utils/auth"; // headers里面必须传递的token
export default {
name: 'vue-upload',
props: {
accept: {
type: Object,
default: null,
},
// 上传地址
url: {
type: String,
default: '',
},
// 上传最大数量 默认为100
fileNumLimit: {
type: Number,
default: 100,
},
// 大小限制 默认2M
fileSingleSizeLimit: {
type: Number,
default: 2048000,
},
// 上传时传给后端的参数,一般为token,key等
formData: {
type: Object,
default: null
},
// 生成formData中文件的key,下面只是个例子,具体哪种形式和后端商议
keyGenerator: {
type: Function,
default(file) {
const currentTime = new Date().getTime();
const key = `${currentTime}.${file.name}`;
return key;
},
},
multiple: {
type: Boolean,
default: false,
},
// 上传按钮ID
uploadButton: {
type: String,
default: '',
},
},
data() {
return {
uploader: null
};
},
mounted() {
this.initWebUpload();
},
methods: {
initWebUpload() {
this.uploader = WebUploader.create({
auto: true, // 选完文件后,是否自动上传
// swf: '/static/lib/webuploader/Uploader.swf', // swf文件路径 非必要
server: this.url, // 文件接收服务端
pick: {
id: this.uploadButton, // 选择文件的按钮
multiple: this.multiple, // 是否多文件上传 默认false
label: '',
},
accept: this.getAccept(this.accept), // 允许选择文件格式。
threads: 3,
fileNumLimit: this.fileNumLimit, // 限制上传个数
//fileSingleSizeLimit: this.fileSingleSizeLimit, // 限制单个上传图片的大小
formData: this.formData, // 上传所需参数
chunked: true, //分片上传
chunkSize: 2048000, //分片大小
duplicate: true, // 重复上传
headers: { Authorization: "Bearer " + getToken() },
});
// 当有文件被添加进队列的时候,添加到页面预览
this.uploader.on('fileQueued', (file) => {
this.$emit('fileChange', file);
});
this.uploader.on('uploadStart', (file) => {
// 在这里可以准备好formData的数据
//this.uploader.options.formData.key = this.keyGenerator(file);
});
// 文件上传过程中创建进度条实时显示。
this.uploader.on('uploadProgress', (file, percentage) => {
this.$emit('progress', file, percentage);
});
this.uploader.on('uploadSuccess', (file, response) => {
this.$emit('success', file, response);
});
this.uploader.on('uploadError', (file, reason) => {
console.error(reason);
this.$emit('uploadError', file, reason);
});
this.uploader.on('error', (type) => {
let errorMessage = '';
if (type === 'F_EXCEED_SIZE') {
errorMessage = `文件大小不能超过${this.fileSingleSizeLimit / (1024 * 1000)}M`;
} else if (type === 'Q_EXCEED_NUM_LIMIT') {
errorMessage = '文件上传已达到最大上限数';
} else {
errorMessage = `上传出错!请检查后重新上传!错误代码${type}`;
}
console.error(errorMessage);
this.$emit('error', errorMessage);
});
this.uploader.on('uploadComplete', (file, response) => {
this.$emit('complete', file, response);
});
},
upload(file) {
this.uploader.upload(file);
},
stop(file) {
this.uploader.stop(file);
},
// 取消并中断文件上传
cancelFile(file) {
this.uploader.cancelFile(file);
},
// 在队列中移除文件
removeFile(file, bool) {
this.uploader.removeFile(file, bool);
},
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'
};
break;
case 'video':
return {
title: 'Videos',
exteensions: 'mp4',
mimeTypes: '.mp4'
};
break;
case 'image':
return {
title: 'Images',
exteensions: 'gif,jpg,jpeg,bmp,png',
mimeTypes: '.gif,.jpg,.jpeg,.bmp,.png'
};
break;
default: return accept
}
},
},
};
</script>
<style lang="scss">
.webuploader-container {
position: relative;
}
.webuploader-element-invisible {
position: absolute !important;
clip: rect(1px 1px 1px 1px); /* IE6, IE7 */
clip: rect(1px,1px,1px,1px);
}
.webuploader-pick {
position: relative;
display: inline-block;
cursor: pointer;
background: #00b7ee;
padding: 10px 15px;
color: #fff;
text-align: center;
border-radius: 3px;
overflow: hidden;
}
.webuploader-pick-hover {
background: #00a2d4;
}
.webuploader-pick-disable {
opacity: 0.6;
pointer-events:none;
}
</style>
- 使用组件
<el-form-item v-show="form.struct == 'single'&&form.courseType == 2" label="视频文件">
<div class="page">
<div id="filePicker">选择文件</div>
<div class="file-panel">
<div class="file-list">
<ul class="file-item" :class="`file-${file.id}`" v-for="file in fileList1" v-show="!form.videoUrl">
<li class="file-type" :icon="fileCategory(file.ext)"></li>
<li class="file-name">{{file.name}}</li>
<li class="file-size">{{fileSize(file.size)}}</li>
<li class="file-status">上传中...</li>
<li class="progress"></li>
<li @click="remove1(file)">关闭</li>
</ul>
<div class="no-file" v-if="!fileList1.length&&!form.videoUrl"><i class="iconfont icon-empty-file"></i> 暂无待上传文件</div>
<div class="no-file" v-if="form.videoUrl">
<i class="iconfont icon-empty-file"></i> {{form.videoUrl}}
<button @click="form.videoUrl=''">删除</button>
</div>
</div>
</div>
<vue-upload
:url="url1"
ref="uploader"
uploadButton="#filePicker"
@fileChange="fileChange"
@progress="onProgress"
@success="onSuccess"
></vue-upload>
</div>
</el-form-item>
data数据
fileList1: [],
url1:process.env.VUE_APP_BASE_API + "/file?type=1",
computed: {
uploader() {
return this.$refs.uploader;
}
},
methods:
fileChange(file) {
if(this.fileList1.length){return this.$message.warning('一次只能上传一个文件')}
if (!file.size) return;
this.form.videoUrl=''
this.fileList1.push(file);
console.log(file);
},
onProgress(file, percent) {
$(`.file-${file.id} .progress`).css('width', percent * 99 + '%');
$(`.file-${file.id} .file-status`).html((percent * 99).toFixed(2) + '%');
},
onSuccess (file, response) {
console.log('上传成功', response);
this.fileList1=[]
$(`.file-${file.id} .file-status`).html( '100%');
this.form.videoUrl = response.data.url
},
resume(file) {
this.uploader.upload(file);
},
stop(file) {
this.uploader.stop(file);
},
remove1(file) {
// 取消并中断文件上传
this.uploader.cancelFile(file);
// 在队列中移除文件
this.uploader.removeFile(file, true);
// 在ui上移除
let index = this.fileList1.findIndex(ele => ele.id === file.id);
this.fileList1.splice(index, 1);
},
fileSize(size) {
return WebUploader.Base.formatSize(size);
},
fileCategory(ext) {
let type = '';
const typeMap = {
image: ['gif', 'jpg', 'jpeg', 'png', 'bmp', 'webp'],
video: ['mp4', 'm3u8', 'rmvb', 'avi', 'swf', '3gp', 'mkv', 'flv'],
text: ['doc', 'txt', 'docx', 'pages', 'epub', 'pdf', 'numbers', 'csv', 'xls', 'xlsx', 'keynote', 'ppt', 'pptx']
};
Object.keys(typeMap).forEach((_type) => {
const extensions = typeMap[_type];
if (extensions.indexOf(ext) > -1) {
type = _type
}
});
return type
},
css
<style lang="scss">
$h-row: 50px;
.file-panel {
width: 100%;
margin-top: 10px;
box-shadow: 0 2px 12px 1px rgba(0, 0, 0, 0.1);
> h2 {
height: 40px;
line-height: 40px;
padding: 0 10px;
border-radius: 4px 4px 0 0;
border-bottom: 1px solid #ccc;
background-color: #fff;
}
.file-list {
position: relative;
height: 100px;
overflow-y: auto;
background-color: rgb(250, 250, 250);
}
.file-item {
position: relative;
height: $h-row;
line-height: $h-row;
padding: 0 10px;
border-bottom: 1px solid #ccc;
background-color: #fff;
z-index: 1;
> li {
display: inline-block;
}
}
.file-type {
width: 24px;
height: 24px;
vertical-align: -5px;
}
.file-name {
width: 40%;
margin-left: 10px;
}
.file-size {
width: 20%;
}
.file-status {
width: 20%;
}
.file-operate {
width: 10%;
> a {
padding: 10px 5px;
cursor: pointer;
color: #666;
&:hover {
color: #ff4081;
}
}
}
.file-type[icon=text] {
// background: url(../../assets/images/icon/text-icon.png);
}
.file-type[icon=video] {
// background: url(../../assets/images/icon/video-icon.png);
}
.file-type[icon=image] {
// background: url(../../assets/images/icon/image-icon.png);
}
.progress {
position: absolute;
top: 0;
left: 0;
height: $h-row - 1;
width: 0;
background-color: #E2EDFE;
z-index: -1;
}
.no-file {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 16px;
}
}
</style>
-
结果图
-
参考:
后端参考:https://glory.blog.csdn.net/article/details/114284285
参考链接:https://www.jb51.net/article/136113.htm