学习目标:
自定义文件上传组件
学习内容:
代码如下
<template>
<div>
<div class="container">
<div class="handle-box">
<div
id="extend-upload-chooseFile"
style="float: left;margin: 0px 10px;"
v-show="showOperationBtn1"
>
<i class="el-icon-plus"></i>
选择文件
</div>
<el-button
type="success"
:icon="uploadStaus"
size="small"
class="handle-del mr10"
@click="uploadToServer()"
:disabled="uploadBtnDisabled"
v-show="showOperationBtn2"
>开始上传</el-button>
<el-button
type="danger"
icon="el-icon-close"
size="small"
class="handle-del mr10"
@click="clearFiles()"
:disabled="uploadBtnDisabled"
v-show="showOperationBtn3"
>清空文件</el-button>
</div>
<div class="showMsg">支持上传的文件后缀:{{ options.fileType }}</div>
<div class="showMsg">支持上传数:{{ fileNumLimit }}</div>
<el-table :data="fileListData" style="width: 100%">
<el-table-column prop="fileName" label="文件名称" width="180" align="center"></el-table-column>
<el-table-column prop="fileSize" label="文件大小" width="120" align="center"></el-table-column>
<el-table-column prop="progress" label="进度" align="center"></el-table-column>
<el-table-column label="操作" width="200" fixed="right" align="center">
<template v-slot="scope">
<el-button
type="text"
class="red"
:icon="scope.row.suspendFileContent === '暂停' ? 'el-icon-sort' : 'el-icon-caret-right'"
@click="suspendRow(scope.$index, scope.row)"
v-show="showOperationBtn4"
>{{ scope.row.suspendFileContent }}</el-button>
<el-button
type="text"
icon="el-icon-close"
class="red"
@click="removeRow(scope.$index, scope.row)"
v-show="showOperationBtn5"
>移除</el-button>
<el-button
type="text"
icon="el-icon-search"
class="red"
@click="seeFile(scope.$index, scope.row)"
v-show="showOperationBtn6"
>查看</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
import $ from 'jquery'
import webUploader from 'webuploader'
export default {
name: 'webupload',
props: {
headers: {
type: String,
default: ''
},
fileNumLimit: {
type: Number,
default: 500
},
fileSize: {
type: Number,
default: 100 * 1024 * 1024 * 1024
},
chunkSize: {
type: Number,
default: 5 * 1024 * 1024
},
uploadSuffixUrl: {
type: String,
default: 'http://localhost:8090'
},
options: {
type: Object,
default: {
//允许上传的文件
fileType: "doc,docx,pdf,xls,xlsx,ppt,pptx,gif,jpg,jpeg,bmp,png,rar,zip,mp4,avi",
fileUploadUrl: "/upload/fileRecord/zone/upload",//上传地址
fileCheckUrl: "/upload/fileRecord/zone/upload/md5Check",//检测文件是否存在url
checkChunkUrl: "/upload/fileRecord/zone/upload/md5Check",//检测分片url
mergeChunksUrl: "/upload/fileRecord/zone/upload/merge/",//合并文件请求地址
headers: {}
}
},
fileListData: {
type: Array,
default: []
},
showUploadBtn: {
type: String,
default: "123456"
},
fileCode: {
type: String,
default: "siteDocument"
}
},
data() {
return {
uploader: {},
uploadBtnDisabled: false,
uploadStaus: "el-icon-upload",
fList: [{
numbers: 1,
fileName: "",
fileSize: "120",
progress: "20%",
oper: ""
}],//文件集合
showOperationBtn1: this.showUploadBtn.lastIndexOf("1") > -1,
showOperationBtn2: this.showUploadBtn.lastIndexOf("2") > -1,
showOperationBtn3: this.showUploadBtn.lastIndexOf("3") > -1,
showOperationBtn4: this.showUploadBtn.lastIndexOf("4") > -1,
showOperationBtn5: this.showUploadBtn.lastIndexOf("5") > -1,
showOperationBtn6: this.showUploadBtn.lastIndexOf("6") > -1,
}
},
mounted() {
this.register();
this.createUploader();
this.events();
},
methods: {
createUploader() {
var fileType = this.options.fileType;
this.uploader = webUploader.create({
// 不压缩image
resize: false,
// swf文件路径
swf: '../../../assets/Uploader.swf', // swf文件路径
// 默认文件接收服务端。
server: this.uploadSuffixUrl + this.options.fileUploadUrl,
pick: {
id: '#extend-upload-chooseFile',//指定选择文件的按钮容器,不指定则不创建按钮。注意 这里虽然写的是 id, 不仅支持 id, 还支持 class, 或者 dom 节点。
multiple: true //开启文件多选
},
// fileSingleSizeLimit:fileSize,//单个文件大小
accept: [{
title: 'file',
extensions: fileType,
mimeTypes: this.buildFileType(fileType)
}],
// 单位字节,如果图片大小小于此值,不会采用压缩。512k 512*1024,如果设置为0,原图尺寸大于设置的尺寸就会压缩;如果大于0,只有在原图尺寸大于设置的尺寸,并且图片大小大于此值,才会压缩
compressSize: 0,
fileNumLimit: this.fileNumLimit,//验证文件总数量, 超出则不允许加入队列,默认值:undefined,如果不配置,则不限制数量
fileSizeLimit: 100 * 1024 * 1024 * 1024, //1kb=1024*1024,验证文件总大小是否超出限制, 超出则不允许加入队列。
fileSingleSizeLimit: this.fileSize, //验证单个文件大小是否超出限制, 超出则不允许加入队列。
chunked: true,//是否开启分片上传
threads: 1,
chunkSize: this.chunkSize,//如果要分片,每一片的文件大小
prepareNextFile: false,//在上传当前文件时,准备好下一个文件,请设置成false,不然开启文件多选你浏览器会卡死
formData: {
fileCode: this.fileCode
}
});
},
register() {//注册函数
var uploadSuffixUrl = this.uploadSuffixUrl;
var that = this;
var options = this.options;
var headers = options.headers || {};
var fileCheckUrl = uploadSuffixUrl + options.fileCheckUrl;//检测文件是否存在url
var checkChunkUrl = uploadSuffixUrl + options.checkChunkUrl;//检测分片url
var mergeChunksUrl = uploadSuffixUrl + options.mergeChunksUrl;//合并文件请求地址
var fileCode = this.fileCode
//监控文件上传的三个时间点(注意:该段代码必须放在WebUploader.create之前)
//时间点1::所有分块进行上传之前(1.可以计算文件的唯一标记;2.可以判断是否秒传)
//时间点2: 如果分块上传,每个分块上传之前(1.询问后台该分块是否已经保存成功,用于断点续传)
//时间点3:所有分块上传成功之后(1.通知后台进行分块文件的合并工作)
webUploader.Uploader.register({
"before-send-file": "beforeSendFile",
"before-send": "beforeSend",
"after-send-file": "afterSendFile"
}, {
//时间点1::所有分块进行上传之前调用此函数
beforeSendFile: function (file) {//利用md5File()方法计算文件的唯一标记符
//创建一个deffered
var deferred = webUploader.Deferred();
//1.计算文件的唯一标记,用于断点续传和秒传,获取文件前20m的md5值,越小越快,防止碰撞,把文件名文件大小和md5拼接作为文件唯一标识
(new webUploader.Uploader()).md5File(file, 0, 10 * 1024 * 1024).progress(function (percentage) {
}).then(function (val) {
file.fileMd5 = val;
//2.请求后台是否保存过该文件,如果存在,则跳过该文件,实现秒传功能
$.ajax({
type: "POST",
url: fileCheckUrl,
headers: headers,
data: {
checkType: 1,
contentType: file.type,
zoneTotalMd5: val,
fileCode: fileCode
},
dataType: "json",
success: (response) => {
if (response.status == 0) {
that.$emit('fileInfo', response.data)
that.uploader.skipFile(file);
that.setTableBtn(file.id, "文件秒传", response.data.networkPath);
that.$notify.success({
showClose: true,
message: `[ ${file.name} ]文件秒传`
});
//如果存在,则跳过该文件,秒传成功
that.fList.push(response.data);
deferred.reject();
} else {
if (response.code === 30001) {//判断是否支持此类文件上传
var m = response.message + ",文件后缀:" + file.ext;
that.uploader.skipFile(file);
that.setTableBtn(file.id, m);
//如果存在,则跳过该文件,秒传成功
that.fList.push(response.data);
deferred.reject();
} else {
//继续上传
deferred.resolve();
}
}
}
});
});
//返回deffered
return deferred.promise();
},
//时间点2:如果有分块上传,则 每个分块上传之前调用此函数
//block:代表当前分块对象
beforeSend: function (block) {//向后台发送当前文件的唯一标记,用于后台创建保存分块文件的目录
//1.请求后台是否保存过当前分块,如果存在,则跳过该分块文件,实现断点续传功能
var deferred = webUploader.Deferred();
//请求后台是否保存完成该文件信息,如果保存过,则跳过,如果没有,则发送该分块内容
(new webUploader.Uploader()).md5File(block.file, block.start, block.end).progress(function (percentage) {
}).then(function (val) {
block.zoneMd5 = val;
$.ajax({
type: "POST",
url: checkChunkUrl,
headers: headers,
data: {
checkType: 2,
zoneTotalMd5: block.file.fileMd5,
zoneMd5: block.zoneMd5,
fileCode: fileCode
},
dataType: "json",
success: function (response) {
if (response.status == 0) {
//分块存在,跳过该分块
deferred.reject();
} else {
//分块不存在或者不完整,重新发送该分块内容
deferred.resolve();
}
}
}
);
});
return deferred.promise();
},
//时间点3:所有分块上传成功之后调用此函数
afterSendFile: function (file) {//前台通知后台合并文件
//1.如果分块上传,则通过后台合并所有分块文件
//请求后台合并文件
$.ajax({
type: "POST",
url: mergeChunksUrl + file.fileMd5,
headers: headers,
dataType: "JSON",
success: function (response) {
if (response.status == 0) {//存在了,+1
var data = response.data.fileInfo;
that.uploader.skipFile(file);
that.setTableBtn(file.id, '上传成功', data.networkPath);
that.fList.push(data);
that.$emit('fileInfo', data)
} else {
console.log(response.message);
}
}
});
}
});
},
events() {
var that = this;
var uplaod = this.uploader;
//当文件添加进去
uplaod.on('fileQueued', function (file) {
var fileSize = that.formatFileSize(file.size);
var row = { fileId: file.id, fileName: file.name, fileSize: fileSize, validateMd5: '0%', progress: "等待上传", state: '就绪', suspendFileContent: '暂停' };
console.log("this.fileListData:", that.fileListData);
that.fileListData.push(row);
});
//监听进度条,更新进度条信息
uplaod.on('uploadProgress', function (file, percentage) {
that.$emit('uploadProgress', (percentage * 100).toFixed(2))
that.setTableBtn(file.id, (percentage * 100).toFixed(2) + '%');
});
/**上传之前**/
uplaod.on('uploadBeforeSend', function (block, data, headers) {
data.fileMd5 = block.file.fileMd5;
data.contentType = block.file.type;
data.chunks = block.file.chunks;
data.zoneTotalMd5 = block.file.fileMd5;
data.zoneMd5 = block.zoneMd5;
data.zoneTotalCount = block.chunks;
data.zoneNowIndex = block.chunk;
data.zoneTotalSize = block.total;
data.zoneStartSize = block.start;
data.zoneEndSize = block.end;
headers.Authorization = that.options.headers.Authorization;
});
//错误信息监听
uplaod.on('error', function (handler) {
if (handler == 'F_EXCEED_SIZE') {
that.$message.error({
showClose: true,
dangerouslyUseHTMLString: true,
message: '上传的单个太大!<br />最大支持' + that.formatFileSize(that.fileSize) + '! <br />操作无法进行,如有需求请联系管理员'
});
} else if (handler == 'Q_TYPE_DENIED') {
that.$message.error({
showClose: true,
dangerouslyUseHTMLString: true,
message: '不允许上传此类文件。<br />操作无法进行,如有需求请联系管理员'
});
}
});
/**从文件队列移除**/
uplaod.on('fileDequeued', function (file) {
// delete percentages[ file.id ];
});
//当文件上传成功时触发。file {ArchivesFile} File对象, response {Object}服务端返回的数据
uplaod.on('uploadSuccess', function (file, response) {
// debugger;
if (response.status == 0) {
that.setTableBtn(file.id, '正在校验文件...');
}
})
//所有文件上传成功后
uplaod.on('uploadFinished', () => {//成功后
that.uploadBtnDisabled = false;
that.uploadStaus = "el-icon-upload";
that.$message.success({
showClose: true,
message: '文件上传结束'
});
that.$emit('uploadBtnDisabled', this.uploadBtnDisabled)
});
uplaod.on('all', function (type) {
// console.log(type);
var state = ''
//监听事件类型
if (type === 'startUpload') {
state = 'uploading';
window.onbeforeunload = function (event) {
if (state === 'uploading' || state === 'paused') {
// console.log("你要放弃本次上传,离开页面吗?");
}
}
} else if (type === 'stopUpload') {
state = 'paused';
} else if (type === 'uploadFinished') {
state = 'done';
}
//按钮字体切换
if (state === 'uploading') {
// $btn.text('暂停');
} else {
// $btn.text('开始');
}
});
},
setTableBtn(fileId, showmsg, networkPath) {
var fileList = this.fileListData;
for (var i = 0; i < fileList.length; i++) {
if (fileList[i].fileId == fileId) {
this.fileListData[i].progress = showmsg;
this.fileListData[i].networkPath = networkPath || "";
}
}
},
suspendRow(index, row) {
if (row.progress === '等待上传') {
this.$message.warning("文件等待上传")
return;
}
if (row.progress === '上传成功' || row.progress === '文件秒传') {
this.$message.warning("文件已上传")
return;
}
if (row.progress === '正在校验文件...') {
this.$message.warning("正在校验文件")
return;
}
if (row.suspendFileContent === '暂停') {
if (this.uploader) {
this.uploader.stop(true);
}
row.suspendFileContent = '继续'
} else {
if (this.uploader) {
this.uploader.upload();
}
row.suspendFileContent = '暂停'
}
},
removeRow(index, row) {
if (this.uploader) {
this.uploader.removeFile(row.fileId);
}
this.fileListData.splice(index, 1);
},
seeFile(index, row) {
var npath = row.networkPath;
if (this.strIsNull(npath)) {
this.$message.error({
showClose: true,
message: '文件未上传,请等待文件上传完成后,方可查看'
});
return;
}
var filens = npath.substring(npath.lastIndexOf(".") + 1);
if (filens == "png" || filens == "jpg" || filens == "jpeg"
|| filens == "gif") {//图片,在当前窗口查看
this.$message.success({
dangerouslyUseHTMLString: true,
showClose: true,
message: `<img src="${this.uploadSuffixUrl + npath}" style="max-width: 80%;max-height: 80%;"/>`
});
} else {
window.open(this.uploadSuffixUrl + npath);
}
},
uploadToServer() {
if (this.fileListData.length <= 0) {
this.$message.error({
showClose: true,
message: '没有上传的文件'
});
return;
}
this.uploadBtnDisabled = true;
this.$emit('uploadBtnDisabled', this.uploadBtnDisabled)
this.uploadStaus = "el-icon-loading";
$("#extent-button-uploader").text("正在上传,请稍等...");
// $("#extent-button-uploader").addClass('layui-btn-disabled');
this.uploader.upload();
},
clearFiles() {
var that = this;
if (that.fileListData.length > 0) {
that.uploadBtnDisabled = false;
that.uploadStaus = "el-icon-upload";
that.uploader.reset();
that.fileListData.length = 0;
}
this.$emit('uploadProgress', 0)
},
beginUploadFile(index, row) {
this.uploadBtnDisabled = false;
this.$emit('uploadBtnDisabled', this.uploadBtnDisabled)
this.uploadStaus = "el-icon-upload";
this.uploader.upload();
},
stopUploadFile(index, row) {
this.uploadBtnDisabled = false;
this.$emit('uploadBtnDisabled', this.uploadBtnDisabled)
this.uploadStaus = "el-icon-upload";
this.uploader.stop();
},
buildFileType(fileType) {
var ts = fileType.split(',');
var ty = '';
for (var i = 0; i < ts.length; i++) {
ty = ty + "." + ts[i] + ",";
}
return ty.substring(0, ty.length - 1)
},
strIsNull(str) {
if (typeof str == "undefined" || str == null || str == "")
return true;
else
return false;
},
formatFileSize(size) {
var fileSize = 0;
if (size / 1024 > 1024) {
var len = size / 1024 / 1024;
fileSize = len.toFixed(2) + "MB";
} else if (size / 1024 / 1024 > 1024) {
var len = size / 1024 / 1024;
fileSize = len.toFixed(2) + "GB";
} else {
var len = size / 1024;
fileSize = len.toFixed(2) + "KB";
}
return fileSize;
},
}
}
</script>
<style>
.container {
padding: 30px;
background: #fff;
border: 1px solid #ddd;
border-radius: 5px;
}
.handle-box {
margin-bottom: 20px;
}
#picker div:nth-child(2) {
width: 100% !important;
height: 100% !important;
}
.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: #409eff;
padding: 8px 12px;
color: #fff;
font-size: 10px;
text-align: center;
border-radius: 3px;
overflow: hidden;
}
.webuploader-pick-hover {
background: #409eff;
}
.showMsg {
margin: 5px;
background: radial-gradient(#d2b8b8, transparent);
}
</style>
引用组件
<WebUpload
ref="webuploader"
:fileNumLimit="fileNumLimit"
:fileSize="fileSize"
:chunkSize="chunkSize"
:uploadSuffixUrl="uploadSuffixUrl"
:options="options"
:fileListData="fileList"
:fileCode="fileCode"
@fileInfo="fileInfo"
@uploadBtnDisabled="uploadBtnDisabled"
@uploadProgress="uploadProgress"
></WebUpload>
效果图
学习时间:
2022-01-14
总结
还待完善…