1.背景: vue-simple-uploader 用于进行文件夹的上传,且项目要支持上传大文件,elementUI并不支持上传文件夹,所以使用vue-simple-uploader。
2.特点:
- 支持文件、多文件、文件夹上传;支持拖拽文件、文件夹上传;
- 可暂停、继续上传、分块上传;
- 文档:https://github.com/simple-uploader/Uploader/blob/develop/README_zh-CN.md
3.vue-simple-uploader的安装:
- 命令:npm install vue-simple-uploader --save
- 在main.js 中
import uploader from 'vue-simple-uploader'; Vue.use(uploader);
- 配置选项:
options: { target: 'http://localhost:8080', // SpringBoot后台接收文件夹数据的接口 simultaneousUploads: 10, // 支持同时上传数量 autoStart: false, // 自动上传 panelShow: false, allowDuplicateUploads: false, // 上传过得文件不可以再上传 testChunks: false, // 是否分片-不分片 chunkSize: '102400000000',//块大小 //query参数是带有数据的post的额外参数,policy、OSSAccessKeyId和signature是获取到的后端签名返回,success_action_status需设置为 200 query: (file) => { return { name: file.name, key: file.key, policy, OSSAccessKeyId: accessId, signature, success_action_status: 200, }; }, }
4.常用的方法
- fileAdded(file, event): 用于文件的验证,可以判断可接受的文件类型,不支持的格式会返回false,拒绝上传,添加文件时,同样也可使用浏览器的event对象;
- filesAdded(files, fileList, event): 和.fileAdded相同,不过是用于多文件的验证;
-
fileSuccess(rootFile, file, message, chunk):完成一个特定的文件上传,rootFile是根文件夹,file是要上传的文件对象,message是服务器返回的响应信息,第四个参数' chunk '是' Uploader.Chunk '的实例;
-
complete():所有的文件都上传成功;
-
fileProgress(rootFile): 获取文件上传的进度,rootFile是所有文件;
-
fileError(rootFile, file, message, chunk):特定的文件上传失败,可在这个回调函数中给用户提示
5.使用:
1.DOM部分
<template>
<!-- 上传器 -->
<uploader ref='uploader' :options='options' :autoStart=false :file-status-text='fileStatusText'
@file-added='onFileAdded' @file-success='onFileSuccess' @file-removed="filesRemove" @file-error='onFileError'
class='uploader-ui'>
<uploader-unsupport></uploader-unsupport>
<uploader-drop>
<div>
<uploader-btn id='global-uploader-btn' :attrs='attrs' ref='uploadBtn'>
选择文件
<i class='el-icon-upload el-icon--right'></i>
</uploader-btn>
</div>
</uploader-drop>
<uploader-list></uploader-list>
</uploader>
</template>
2.script部分
<script>
export default {
data() {
return {
options: {
//目标上传 URL,默认POST
target: process.env.VUE_APP_BASE_URL + '/uploader/chunk',
//分块大小(单位:字节)
chunkSize: '2048000',
//上传文件时文件内容的参数名,对应chunk里的Multipart对象名,默认对象名为file
fileParameterName: 'upfile',
//失败后最多自动重试上传次数
maxChunkRetries: 3,
changeOrigin: true,
//是否开启服务器分片校验,对应GET类型同名的target URL
testChunks: true,
headers: { 'Authorization': 'Bearer ' + localStorage.getItem('token') },
/*
服务器分片校验函数,判断秒传及断点续传,传入的参数是Uploader.Chunk实例以及请求响应信息
reponse码是successStatuses码时,才会进入该方法
reponse码如果返回的是permanentErrors 中的状态码,不会进入该方法,直接进入onFileError函数 ,并显示上传失败
reponse码是其他状态码,不会进入该方法,正常走标准上传
checkChunkUploadedByResponse函数直接return true的话,不再调用上传接口
*/
checkChunkUploadedByResponse: function (chunk, response_msg) {
let objMessage = JSON.parse(response_msg);
if (objMessage.skipUpload) {
return true;
}
return (objMessage.uploadedChunks || []).indexOf(chunk.offset + 1) >= 0;
}
},
attrs: {
accept: ACCEPT_CONFIG.getAll()
},
fileStatusText: {
success: '上传成功',
error: '上传失败',
uploading: '上传中',
paused: '暂停',
waiting: '等待上传'
}
};
},
methods: {
onFileAdded(file) {
this.computeMD5(file);
},
/*
第一个参数 rootFile 就是成功上传的文件所属的根 Uploader.File 对象,它应该包含或者等于成功上传文件;
第二个参数 file 就是当前成功的 Uploader.File 对象本身;
第三个参数就是 message 就是服务端响应内容,永远都是字符串;
第四个参数 chunk 就是 Uploader.Chunk 实例,它就是该文件的最后一个块实例,如果你想得到请求响应码的话,chunk.xhr.status就是
*/
onFileSuccess(rootFile, file, response, chunk) {
//refProjectId为预留字段,可关联附件所属目标,例如所属档案,所属工程等
file.refProjectId = '123456789';
console.log('::::', file);
let data = file
data.filename = file.name
data.identifier = file.uniqueIdentifier
data.totalSize = file.size
data.alluxioPath = this.sendContent.alluxioPath
data.userStorageId = this.sendContent.id
console.log('file', file, data)
mergeFile(data).then(res => {
console.log('23342', res)
console.log('父组件', this.$parent)
// if (res.code === 415) {
// console.log('合并操作未成功,结果码:' + res.code);
// }
if (res.code == 200) {
if (this.sendContent.idArray && this.sendContent.idArray.length > 0) {
this.sendContent.idArray.forEach((item) => {
// this.$parent.refreshList(item)
console.log('aaaaaaaaa',item)
this.$emit('refresh', item)
this.$emit('getFather')
})
this.$message.success(res.message)
}
} else {
this.$message.error(res.message)
}
})
// .catch(function (error) {
// console.log('合并后捕获的未知异常:' + error);
// });
},
filesRemove(file) {
// this.$refs.uploader.pause();
file.paused = true
let data = file
data.filename = file.name
data.identifier = file.uniqueIdentifier
data.totalSize = file.size
data.alluxioPath = this.sendContent.alluxioPath
data.userStorageId = this.sendContent.id
deleteMkFile(data).then((res) => {
console.log('delete', res)
if (res.code == 200) {
this.$message.success(res.msg)
} else {
this.$message.error(res.msg)
}
})
// const uploaderInstance = this.$refs.uploader.uploader;
// let temp = uploaderInstance.fileList.findIndex(e => e.uniqueIdentifier === file.uniqueIdentifier)
// if (temp > -1) {
// uploaderInstance.fileList[temp].cancel(); //这句代码是删除所选上传文件的关键
// }
},
onFileError(rootFile, file, response, chunk) {
console.log('上传完成后异常信息:' + response);
},
/**
* 计算md5,实现断点续传及秒传
* @param file
*/
computeMD5(file) {
file.pause();
//单个文件的大小限制2G
let fileSizeLimit = 2 * 1024 * 1024 * 1024;
console.log('文件大小:' + file.size);
console.log('限制大小:' + fileSizeLimit);
console.log('alluxioPath' + this.sendContent.alluxioPath);
// if (file.size > fileSizeLimit) {
// this.$message({
// showClose: true,
// message: '文件大小不能超过2G'
// });
// file.cancel();
// }
let fileReader = new FileReader();
let time = new Date().getTime();
let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
let currentChunk = 0;
const chunkSize = 10 * 1024 * 1000;
let chunks = Math.ceil(file.size / chunkSize);
let spark = new SparkMD5.ArrayBuffer();
//由于计算整个文件的Md5太慢,因此采用只计算第1块文件的md5的方式
let chunkNumberMD5 = 1;
loadNext();
fileReader.onload = (e => {
spark.append(e.target.result);
if (currentChunk < chunkNumberMD5) {
loadNext();
} else {
let md5 = spark.end();
file.uniqueIdentifier = md5;
file.resume();
console.log(`MD5计算完毕:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${file.size} 用时:${new Date().getTime() - time} ms`);
}
});
fileReader.onerror = function () {
this.error(`文件${file.name} 读取出错,请检查该文件`);
file.cancel();
};
function loadNext() {
let start = currentChunk * chunkSize;
let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
currentChunk++;
console.log('计算第' + currentChunk + '块');
}
},
close() {
this.uploader.cancel();
},
error(msg) {
this.$notify({
title: '错误',
message: msg,
type: 'error',
duration: 2000
});
}
}
};
</script>