首先解释一下什么是分片上传
分片上传就是把一个大的文件分成若干块,一块一块的传输。这样做的好处可以减少重新上传的开销。比如:如果我们上传的文件是一个很大的文件,那么上传的时间应该会比较久,再加上网络不稳定各种因素的影响,很容易导致传输中断,用户除了重新上传文件外没有其他的办法,但是我们可以使用分片上传来解决这个问题。通过分片上传技术,如果网络传输中断,我们重新选择文件只需要传剩余的分片。而不需要重传整个文件,大大减少了重传的开销。
但是我们要如何选择一个合适的分片呢?因此我们要考虑如下几个事情:
1.分片越小,那么请求肯定越多,开销就越大。因此不能设置太小。
2.分片越大,灵活度就少了。
3.服务器端都会有个固定大小的接收Buffer。分片的大小最好是这个值的整数倍。
分片上传的步骤
1.先对文件进行md5加密。使用md5加密的优点是:可以对文件进行唯一标识,同样可以为后台进行文件完整性校验进行比对。
2.拿到md5值以后,服务器端查询下该文件是否已经上传过,如果已经上传过的话,就不用重新再上传。
3.对大文件进行分片。比如一个100M的文件,我们一个分片是5M的话,那么这个文件可以分20次上传。
4.向后台请求接口,接口里的数据就是我们已经上传过的文件块。(注意:为什么要发这个请求?就是为了能断点续传,比如我们使用百度网盘对吧,网盘里面有续传功能,当一个文件传到一半的时候,突然想下班不想上传了,那么服务器就应该记住我之前上传过的文件块,当我打开电脑重新上传的时候,那么它应该跳过我之前已经上传的文件块。再上传后续的块)。
5.开始对未上传过的文件块进行上传。(这个是第二个请求,会把所有的分片合并,然后上传请求)。
6.上传成功后,服务器会进行文件合并。最后完成。
话不多说,直接开始干代码
<template>
<div>
<!-- on-preview 点击文件列表中已上传的文件时的钩子 -->
<!-- http-request 覆盖默认的上传行为,可以自定义上传的实现 -->
<!-- limit 最大允许上传个数 -->
<!-- before-upload 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。 -->
<!-- accept 接受上传的文件类型(thumbnail-mode 模式下此参数无效) -->
<!-- multiple 是否支持多选文件 -->
<!-- on-change 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用 -->
<!-- on-remove 文件列表移除文件时的钩子 -->
<!-- file-list 上传的文件列表, 例如: [{name: 'food.jpg', url: ''}] -->
<!-- on-exceed 文件超出个数限制时的钩子 -->
<!-- auto-upload 是否在选取文件后立即进行上传 -->
<!-- action 必选参数,上传的地址 例如 action="https://jsonplaceholder.typicode.com/posts/"-->
<el-upload
drag
multiple
:auto-upload="true"
:http-request="checkedFile"
:before-remove="removeFile"
:limit="10"
action="/tools/upload_test/"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处,或
<em>点击上传</em>
</div>
</el-upload>
<el-progress type="circle" :percentage="progress" class="progress" v-if="showProgress"></el-progress>
</div>
</template>
文件上传时,会走http-request方法,如果定义了这个方法,组件的submit
方法就会被拦截掉(注意别在这个方法里面调用组件的submit
方法,会造成死循环),在这个方法里面我就可以搞我想搞的事情了。
http-request 这个传入的回调函数应该返回一个Promise
,所以我自己定义了一个无用的Promise进去
const prom = new Promise((resolve, reject) => {})
prom.abort = () => {}
return prom
如果要实现断点续传,需要和后端确定好,如何配合。
我这里的方案是,在我把所有的分片全部上传一遍后,会请求一个查询接口,后端在这个接口里面返回给我哪些分片没有上传成功(会给我序号),我这个时候,再去重新上传那些没有被上传成功的分片
直接贴完整代码,注释都在里面,看不懂的可以直接联系我,博客上有联系方式(依赖element-ui、axios、spark-md5)
<template>
<div>
<!-- on-preview 点击文件列表中已上传的文件时的钩子 -->
<!-- http-request 覆盖默认的上传行为,可以自定义上传的实现 -->
<!-- limit 最大允许上传个数 -->
<!-- before-upload 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。 -->
<!-- accept 接受上传的文件类型(thumbnail-mode 模式下此参数无效) -->
<!-- multiple 是否支持多选文件 -->
<!-- on-change 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用 -->
<!-- on-remove 文件列表移除文件时的钩子 -->
<!-- file-list 上传的文件列表, 例如: [{name: 'food.jpg', url: ''}] -->
<!-- on-exceed 文件超出个数限制时的钩子 -->
<!-- auto-upload 是否在选取文件后立即进行上传 -->
<!-- action 必选参数,上传的地址 例如 action="https://jsonplaceholder.typicode.com/posts/"-->
<el-upload
drag
multiple
:auto-upload="true"
:http-request="checkedFile"
:before-remove="removeFile"
:limit="10"
action="/tools/upload_test/"
>
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处,或
<em>点击上传</em>
</div>
</el-upload>
<el-progress type="circle" :percentage="progress" class="progress" v-if="showProgress"></el-progress>
</div>
</template>
<script>
import axios from "axios";
import SparkMD5 from "spark-md5";
export default {
data() {
return {
maxSize: 5 * 1024 * 1024 * 1024, // 上传最大文件限制 最小单位是b
multiUploadSize: 100 * 1024 * 1024, // 大于这个大小的文件使用分块上传(后端可以支持断点续传) 100mb
eachSize: 100 * 1024 * 1024, // 每块文件大小 100mb
requestCancelQueue: [], // 请求方法队列(调用取消上传
url: "/tools/upload_test/",
//上传进度
progress: 0,
showProgress: false,
// 每上传一块的进度
eachProgress: 0,
// 总共有多少块。断点续传使用
chunksKeep:0,
// 切割后的文件数组
fileChunksKeep:[],
// 这个文件,断点续传
fileKeep:null
};
},
mounted() {
},
methods: {
async checkedFile(options) {
console.log(options);
const {
maxSize,
multiUploadSize,
getSize,
splitUpload,
singleUpload
} = this; // 解构赋值
const { file, onProgress, onSuccess, onError } = options; // 解构赋值
if (file.size > maxSize) {
return this.$message({
message: `您选择的文件大于${getSize(maxSize)}`,
type: "error"
});
}
this.fileKeep = file
const uploadFunc =
file.size > multiUploadSize ? splitUpload : singleUpload; // 选择上传方式
try {
await uploadFunc(file, onProgress);
this.$message({
message: "上传成功",
type: "success"
});
this.showProgress = false;
this.progress = 0;
onSuccess();
} catch (e) {
console.error(e);
this.$message({
message: e.message,
type: "error"
});
this.showProgress = false;
this.progress = 0;
onError();
}
const prom = new Promise((resolve, reject) => {}); // 上传后返回一个promise
prom.abort = () => {};
return prom;
},
// 格式化文件大小显示文字
getSize(size) {
return size > 1024
? size / 1024 > 1024
? size / (1024 * 1024) > 1024
? (size / (1024 * 1024 * 1024)).toFixed(2) + "GB"
: (size / (1024 * 1024)).toFixed(2) + "MB"
: (size / 1024).toFixed(2) + "KB"
: size.toFixed(2) + "B";
},
// 单文件直接上传
async singleUpload(file, onProgress) {
await this.postFile(
{ file, uid: file.uid, fileName: file.fileName ,chunk:0},
onProgress
);
var spark = new SparkMD5.ArrayBuffer();
spark.append(file);
var md5 = spark.end();
console.log(md5);
const isValidate = await this.validateFile({
fileName: file.name,
uid: file.uid,
md5:md5,
chunks:1
});
},
// 大文件分块上传
splitUpload(file, onProgress) {
console.log('file11')
console.log(file)
return new Promise(async (resolve, reject) => {
try {
const { eachSize } = this;
const chunks = Math.ceil(file.size / eachSize);
this.chunksKeep = chunks
const fileChunks = await this.splitFile(file, eachSize, chunks);
this.fileChunksKeep = fileChunks
console.log('fileChunks,文件数组切割后')
console.log(fileChunks)
//判断每上传一个文件,进度条涨多少,保留两位小数
this.eachProgress = parseInt(Math.floor(100 / chunks * 100) / 100);
this.showProgress = true;
let currentChunk = 0;
for (let i = 0; i < fileChunks.length; i++) {
// 服务端检测已经上传到第currentChunk块了,那就直接跳到第currentChunk块,实现断点续传
console.log(currentChunk, i);
// 此时需要判断进度条
if (Number(currentChunk) === i) {
// 每块上传完后则返回需要提交的下一块的index
await this.postFile(
{
chunked: true,
chunk: i,
chunks,
eachSize,
fileName: file.name,
fullSize: file.size,
uid: file.uid,
file: fileChunks[i]
},
onProgress
);
currentChunk++
// 上传完一块后,进度条增加
this.progress += this.eachProgress;
// 不能超过100
this.progress = this.progress > 100 ? 100 : this.progress;
}
}
var spark = new SparkMD5.ArrayBuffer();
spark.append(file);
var md5 = spark.end();
console.log(md5);
const isValidate = await this.validateFile({
chunks: fileChunks.length,
// chunk: fileChunks.length,
fileName: file.name,
uid: file.uid,
md5:md5,
// task_id:file.uid
});
// if (!isValidate) {
// throw new Error("文件校验异常");
// }
resolve();
} catch (e) {
reject(e);
}
});
},
againSplitUpload(file, array) {
console.log('file,array')
console.log(file)
console.log(array)
return new Promise(async (resolve, reject) => {
try {
const { eachSize , fileKeep } = this;
const chunks = this.chunksKeep
const fileChunks = this.fileChunksKeep
this.showProgress = true;
// let currentChunk = 0;
for (let i = 0; i < array.length; i++) {
// 服务端检测已经上传到第currentChunk块了,那就直接跳到第currentChunk块,实现断点续传
// console.log(currentChunk, i);
// 此时需要判断进度条
// 每块上传完后则返回需要提交的下一块的index
await this.postFile(
{
chunked: true,
chunk: array[i],
chunks,
fileName: file.fileName,
fullSize: fileKeep.size,
uid: file.uid,
file: fileChunks[array[i]]
},
);
// currentChunk++
// 上传完一块后,进度条增加
// this.progress += this.eachProgress;
// 不能超过100
this.progress = this.progress > 100 ? 100 : this.progress;
}
var spark = new SparkMD5.ArrayBuffer();
spark.append(fileKeep);
var md5 = spark.end();
console.log(md5);
const isValidate = await this.validateFile({
chunks: fileChunks.length,
// chunk: fileChunks.length,
fileName: file.fileName,
uid: file.uid,
md5:md5,
// task_id:file.uid
});
// if (!isValidate) {
// throw new Error("文件校验异常");
// }
resolve();
} catch (e) {
reject(e);
}
});
},
// 文件分块,利用Array.prototype.slice方法
splitFile(file, eachSize, chunks) {
return new Promise((resolve, reject) => {
try {
setTimeout(() => {
const fileChunk = [];
for (let chunk = 0; chunks > 0; chunks--) {
fileChunk.push(file.slice(chunk, chunk + eachSize));
chunk += eachSize;
}
resolve(fileChunk);
}, 0);
} catch (e) {
console.error(e);
reject(new Error("文件切块发生错误"));
}
});
},
removeFile(file) {
this.requestCancelQueue[file.uid]();
delete this.requestCancelQueue[file.uid];
return true;
},
// 提交文件方法,将参数转换为FormData, 然后通过axios发起请求
postFile(param, onProgress) {
console.log(param);
const formData = new FormData();
// for (let p in param) {
// formData.append(p, param[p]);
// }
formData.append('file', param.file) // 改了
formData.append('uid',param.uid)
formData.append('chunk',param.chunk)
const { requestCancelQueue } = this;
const config = {
cancelToken: new axios.CancelToken(function executor(cancel) {
if (requestCancelQueue[param.uid]) {
requestCancelQueue[param.uid]();
delete requestCancelQueue[param.uid];
}
requestCancelQueue[param.uid] = cancel;
}),
onUploadProgress: e => {
if (param.chunked) {
e.percent = Number(
(
((param.chunk * (param.eachSize - 1) + e.loaded) /
param.fullSize) *
100
).toFixed(2)
);
} else {
e.percent = Number(((e.loaded / e.total) * 100).toFixed(2));
}
onProgress(e);
}
};
// return axios.post('/api/v1/tools/upload_test/', formData, config).then(rs => rs.data)
return this.$http({
url: "/tools/upload_test/",
method: "POST",
data: formData
// config
}).then(rs => rs.data);
},
// 文件校验方法
validateFile(file) {
// return axios.post('/api/v1/tools/upload_test/', file).then(rs => rs.data)
console.log(2)
console.log(file)
return this.$http({
url: "/tools/upload_test/upload_success/",
method: "POST",
data: file
}).then(res => {
if(res && res.status == 1){
this.againSplitUpload(file,res.data.error_file)
return true
}
});
}
}
};
</script>
<style scoped>
.progress{
/* 在当前页面居中 */
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
/* 宽度 */
}
</style>
更新代码,上面的代码使用md5加密后,与后端加密的MD5值不一样,下面的加密过后是一样的
<template>
<div :class="showProgress == true ? 'loading' : ''">
<!-- on-preview 点击文件列表中已上传的文件时的钩子 -->
<!-- http-request 覆盖默认的上传行为,可以自定义上传的实现 -->
<!-- limit 最大允许上传个数 -->
<!-- before-upload 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。 -->
<!-- accept 接受上传的文件类型(thumbnail-mode 模式下此参数无效) -->
<!-- multiple 是否支持多选文件 -->
<!-- on-change 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用 -->
<!-- on-remove 文件列表移除文件时的钩子 -->
<!-- file-list 上传的文件列表, 例如: [{name: 'food.jpg', url: ''}] -->
<!-- on-exceed 文件超出个数限制时的钩子 -->
<!-- auto-upload 是否在选取文件后立即进行上传 -->
<!-- action 必选参数,上传的地址 例如 action="https://jsonplaceholder.typicode.com/posts/"-->
<el-upload drag multiple :auto-upload="true" :http-request="checkedFile" :before-remove="removeFile" :limit="10"
action="/tools/upload_chunk/" :disabled="showProgress">
<i class="el-icon-upload"></i>
<div class="el-upload__text">
将文件拖到此处,或
<em>点击上传</em>
</div>
</el-upload>
<!-- 正在上传的弹窗 -->
<el-dialog title="正在上传" :visible.sync="showProgress" width="50%">
<el-progress type="circle" :percentage="progress" class="progress" v-if="showProgress"></el-progress>
</el-dialog>
<!-- <el-progress type="circle" :percentage="progress" class="progress" v-if="showProgress"></el-progress> -->
</div>
</template>
<script>
import axios from "axios";
import SparkMD5 from "spark-md5";
export default {
data() {
return {
maxSize: 5 * 1024 * 1024 * 1024, // 上传最大文件限制 最小单位是b
multiUploadSize: 100 * 1024 * 1024, // 大于这个大小的文件使用分块上传(后端可以支持断点续传) 100mb
eachSize: 100 * 1024 * 1024, // 每块文件大小 100mb
requestCancelQueue: [], // 请求方法队列(调用取消上传
url: "/tools/upload_chunk/",
//上传进度
progress: 0,
showProgress: false,
// 每上传一块的进度
eachProgress: 0,
// 总共有多少块。断点续传使用
chunksKeep: 0,
// 切割后的文件数组
fileChunksKeep: [],
// 这个文件,断点续传
fileKeep: null,
// 断点续传,文件md5
fileMd5Keep: ""
};
},
mounted() { },
methods: {
async checkedFile(options) {
// console.log(options);
const {
maxSize,
multiUploadSize,
getSize,
splitUpload,
singleUpload
} = this; // 解构赋值
const { file, onProgress, onSuccess, onError } = options; // 解构赋值
if (file.size > maxSize) {
return this.$message({
message: `您选择的文件大于${getSize(maxSize)}`,
type: "error"
});
}
this.fileKeep = file;
const uploadFunc =
file.size > multiUploadSize ? splitUpload : singleUpload; // 选择上传方式
try {
await uploadFunc(file, onProgress);
onSuccess();
} catch (e) {
console.error(e);
this.$message({
message: e.message,
type: "error"
});
this.showProgress = false;
this.progress = 0;
onError();
}
const prom = new Promise((resolve, reject) => { }); // 上传后返回一个promise
prom.abort = () => { };
return prom;
},
// 格式化文件大小显示文字
getSize(size) {
return size > 1024
? size / 1024 > 1024
? size / (1024 * 1024) > 1024
? (size / (1024 * 1024 * 1024)).toFixed(2) + "GB"
: (size / (1024 * 1024)).toFixed(2) + "MB"
: (size / 1024).toFixed(2) + "KB"
: size.toFixed(2) + "B";
},
// 单文件直接上传
async singleUpload(file, onProgress) {
await this.postFile(
{ file, uid: file.uid, fileName: file.fileName, chunk: 0 },
onProgress
);
// var spark = new SparkMD5.ArrayBuffer();
// spark.append(file);
// var md5 = spark.end();
// console.log(md5);
const reader = new FileReader();
reader.readAsArrayBuffer(file);
let hashMd5 = "";
console.log(hashMd5);
const that = this;
function getHash(cb) {
console.log("进入单个上传的getHash");
reader.onload = function (e) {
console.log("进入单个上传的getHash的函数2");
console.log(hashMd5);
console.log(this);
// console.log(e)
const hash = SparkMD5.ArrayBuffer.hash(e.target.result);
// const hash = SparkMD5.ArrayBuffer.hash(file);
console.log(hash);
that.hashMd5 = hash;
console.log(that.hashMd5);
that.fileMd5Keep = hash;
cb(hash);
};
}
await getHash(function (hash) {
console.log(hash);
console.log(that);
// 请求接口
that.validateFile({
name: file.name,
uid: file.uid,
md5: hash,
chunks: 1,
filter_type: "user_data_file"
});
});
},
// getMd5(file, chunkCount) {
// const spark = new SparkMD5.ArrayBuffer();
// let currentChunk = 0;
// const reader = new FileReader();
// reader.onload = function(e) {
// spark.append(e.target.result);
// currentChunk++;
// if (currentChunk < chunkCount) {
// console.log(currentChunk);
// loadNext();
// } else {
// console.log(spark.end());
// // 在这里请求接口
// return spark.end();
// }
// };
// function loadNext() {
// const start = currentChunk * chunkSize;
// const end =
// start + chunkSize >= file.size ? file.size : start + chunkSize;
// reader.readAsArrayBuffer(file.slice(start, end));
// }
// loadNext();
// },
// 大文件分块上传
splitUpload(file, onProgress) {
return new Promise(async (resolve, reject) => {
try {
const { eachSize } = this;
const chunks = Math.ceil(file.size / eachSize);
this.chunksKeep = chunks;
const fileChunks = await this.splitFile(file, eachSize, chunks);
this.fileChunksKeep = fileChunks;
console.log("fileChunks,文件数组切割后");
console.log(fileChunks);
//判断每上传一个文件,进度条涨多少,保留两位小数
this.eachProgress = parseInt(Math.floor((100 / chunks) * 100) / 100);
this.showProgress = true;
let currentChunk = 0;
for (let i = 0; i < fileChunks.length; i++) {
// 服务端检测已经上传到第currentChunk块了,那就直接跳到第currentChunk块,实现断点续传
console.log(currentChunk, i);
// 此时需要判断进度条
if (Number(currentChunk) === i) {
// 每块上传完后则返回需要提交的下一块的index
await this.postFile(
{
chunked: true,
chunk: i,
chunks,
eachSize,
fileName: file.name,
fullSize: file.size,
uid: file.uid,
file: fileChunks[i]
},
onProgress
);
currentChunk++;
// 上传完一块后,进度条增加
this.progress += this.eachProgress;
// 不能超过100
this.progress = this.progress > 100 ? 100 : this.progress;
}
}
// this.getMd5(file, chunks);
// var spark = new SparkMD5.ArrayBuffer();
// spark.append(file);
// var md5 = spark.end();
// console.log(md5);
const spark = new SparkMD5.ArrayBuffer();
let currentChunkMd5 = 0;
const that = this;
const reader = new FileReader();
reader.onload = async function (e) {
spark.append(e.target.result);
currentChunkMd5++;
if (currentChunkMd5 < chunks) {
loadNext();
} else {
// console.log(spark.end());
var hashMd5111 = spark.end();
that.fileMd5Keep = hashMd5111;
console.log(that);
console.log(hashMd5111);
// 在这里请求接口
await that.validateFile({
name: file.name,
uid: file.uid,
md5: hashMd5111,
chunks: fileChunks.length,
filter_type: "git_secret_file"
// chunk: fileChunks.length,
});
}
};
async function loadNext() {
const start = currentChunkMd5 * eachSize;
const end =
start + eachSize >= file.size ? file.size : start + eachSize;
await reader.readAsArrayBuffer(file.slice(start, end));
}
this.$message({
message: "正在进行文件加密校验",
type: "info"
});
await loadNext();
// let hashMd5 = "";
// // console.log(hashMd5)
// const that = this;
// console.log("进入分片上传的getHash");
// function getHash(cb) {
// reader.onload = function(e) {
// console.log("进入分片上传的getHash的函数");
// const hash = SparkMD5.ArrayBuffer.hash(e.target.result);
// // const hash = SparkMD5.ArrayBuffer.hash(file);
// console.log(hash);
// that.hashMd5 = hash;
// console.log(that.hashMd5);
// that.fileMd5Keep = hash;
// cb(hash);
// };
// reader.readAsArrayBuffer(file);
// }
// await getHash(function() {
// console.log(that);
// that.validateFile({
// name: file.name,
// uid: file.uid,
// md5: that.hashMd5,
// chunks: fileChunks.length
// // chunk: fileChunks.length,
// });
// });
// 请求接口
// console.log('fileChunks.length')
// 请求接口
// this.validateFile({
// fileName: file.name,
// uid: file.uid,
// md5:md5,
// chunks:1
// });
resolve();
} catch (error) {
reject(error);
}
});
},
// 断点续传
againSplitUpload(file, array) {
console.log("file,array");
console.log(file);
console.log(array);
return new Promise(async (resolve, reject) => {
try {
const { eachSize, fileKeep } = this;
const chunks = this.chunksKeep;
const fileChunks = this.fileChunksKeep;
this.showProgress = true;
// let currentChunk = 0;
for (let i = 0; i < array.length; i++) {
// 服务端检测已经上传到第currentChunk块了,那就直接跳到第currentChunk块,实现断点续传
// console.log(currentChunk, i);
// 此时需要判断进度条
// 每块上传完后则返回需要提交的下一块的index
await this.postFile({
chunked: true,
chunk: array[i],
chunks,
name: file.name,
fullSize: fileKeep.size,
uid: file.uid,
file: fileChunks[array[i]]
});
// currentChunk++
// 上传完一块后,进度条增加
// this.progress += this.eachProgress;
// 不能超过100
this.progress = this.progress > 100 ? 100 : this.progress;
}
// var spark = new SparkMD5.ArrayBuffer();
// spark.append(fileKeep);
// var md5 = spark.end();
// console.log(md5);
var fileMd5KeepTwo = this.fileMd5Keep;
const isValidate = await this.validateFile({
chunks: fileChunks.length,
// chunk: fileChunks.length,
name: file.name,
uid: file.uid,
md5: fileMd5KeepTwo,
filter_type: "git_secret_file"
// task_id:file.uid
});
// if (!isValidate) {
// throw new Error("文件校验异常");
// }
// 关闭进度条
this.showProgress = false;
// 重置进度条
this.progress = 0;
resolve();
} catch (e) {
reject(e);
}
});
},
// 文件分块,利用Array.prototype.slice方法
splitFile(file, eachSize, chunks) {
return new Promise((resolve, reject) => {
try {
setTimeout(() => {
const fileChunk = [];
for (let chunk = 0; chunks > 0; chunks--) {
fileChunk.push(file.slice(chunk, chunk + eachSize));
chunk += eachSize;
}
resolve(fileChunk);
}, 0);
} catch (e) {
console.error(e);
reject(new Error("文件切块发生错误"));
}
});
},
removeFile(file) {
this.requestCancelQueue[file.uid]();
delete this.requestCancelQueue[file.uid];
return true;
},
// 提交文件方法,将参数转换为FormData, 然后通过axios发起请求
postFile(param, onProgress) {
// console.log(param);
const formData = new FormData();
// for (let p in param) {
// formData.append(p, param[p]);
// }
formData.append("file", param.file); // 改了
formData.append("uid", param.uid);
formData.append("chunk", param.chunk);
formData.append("filter_type", "git_secret_file");
const { requestCancelQueue } = this;
const config = {
cancelToken: new axios.CancelToken(function executor(cancel) {
if (requestCancelQueue[param.uid]) {
requestCancelQueue[param.uid]();
delete requestCancelQueue[param.uid];
}
requestCancelQueue[param.uid] = cancel;
}),
onUploadProgress: e => {
if (param.chunked) {
e.percent = Number(
(
((param.chunk * (param.eachSize - 1) + e.loaded) /
param.fullSize) *
100
).toFixed(2)
);
} else {
e.percent = Number(((e.loaded / e.total) * 100).toFixed(2));
}
onProgress(e);
}
};
// return axios.post('/api/v1/tools/upload_chunk/', formData, config).then(rs => rs.data)
return this.$http({
url: "/tools/upload_chunk/",
method: "POST",
data: formData
// config
}).then(rs => rs.data);
},
// 文件校验方法
validateFile(file) {
// return axios.post('/api/v1/tools/upload_chunk/', file).then(rs => rs.data)
return this.$http({
url: "/tools/upload_chunk/upload_success/",
method: "POST",
data: file
}).then(res => {
if (res && res.status == 1) {
this.againSplitUpload(file, res.data.error_file);
this.$message({
message: "有文件上传失败,正在重新上传",
type: "warning"
});
} else if (res && res.status == 0) {
this.$message({
message: "上传成功",
type: "success"
});
this.showProgress = false;
this.progress = 0;
} else if (res && res.status == 40008) {
this.$message.error(res.message);
this.showProgress = false;
this.progress = 0;
}
});
}
}
};
</script>
<style scoped>
.loading {
/* 整体页面置灰 */
/* background: rgba(0, 0, 0, 0.5); */
}
.progress {
/* 在当前页面居中 */
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
margin-top: 40px;
/* 宽度 */
}
/deep/ .el-dialog {
position: relative;
height: 500px;
}
</style>