<template> <div> <input type="file" @change="uploadChange" multiple="multiple" /> <div style="text-align: left"> <div v-for="(item, index) in waitList" :key="item.lastModified + item.name" style="margin-bottom: 20px" > <div style="margin: 5px"> <button @click="startUploadFile(item, index)">开始上传</button> <span>{{ item.name }}</span> </div> </div> <div v-for="item in startList" :key="item.lastModified + item.name"> <span>{{ item.name }}</span> <div style="width: 600px; height: 4px; background: gray"> <div :style="{ background: 'blue', height: '4px', width: item.precent ? item.precent + '%' : '0px', }" ></div> </div> {{ item.precent }}% <div v-if="item.uploadStatus !== 'success'"> <div v-if="item.isStop" style="margin: 20px" @click="item.startUpload()" class="pointer" > 继续 </div> <div v-else style="margin: 20px" @click="item.stopUpload()" class="pointer" > 暂停 </div> </div> </div> </div> </div></template><script>import axios from "axios";// axios.defaults.baseURL = 'http://localhost';import CryptoJs from "crypto-js";export default { data() { return { chunkSize: 1024 * 1024 * 1, // 50M fileList: [], fileInfoList: [], waitList: [], //存储所有的上传文件 startList: [], //开始上传的分片 endList: [], //已上传的分片 }; }, methods: { sendFile(file, sourceToken) { //文件不大于的单独上传 let formdata = new FormData(); formdata.append("file", file); formdata.append("suffix", file.name.split(".").pop().toLowerCase()); axios({ url: "/test/upload", method: "post", data: formdata, headers: { "Content-Type": "multipart/form-data" }, cancelToken: sourceToken, onUploadProgress: (e) => { //获取上传文件得进度 file.uploadedChunkSize = e.loaded; file.precent = ((e.loaded / e.total) * 100).toFixed(2); if (file.precent == 100) { // this.endUploadFile(file); } this.$forceUpdate(); // 刷新视图 }, }).then( ({ data }) => { console.log(data, "upload/file"); }, (err) => { console.log(err); } ); }, startUploadFile(item, index) { //开始上传 this.waitList.splice(index, 1); //移除上传的文件 this.startList.push(item); //将需要上传的文件,放入数组 this.uploadFile(item); //上传 }, endUploadFile(item, index) { this.startList.splice(index, 1); this.endList.push(item); }, uploadChange(e) { const files = e.target.files; this.waitList = [...this.waitList, ...files]; }, async uploadFile(file) { file.precent = 0; // 百分比进度 file.uploadedChunkSize = 0; // 记录上传切片大小 file.uploadStartTime = new Date().getTime; file.isStop = false; //文件进度 file.hash = CryptoJs.MD5(file.name + file.uploadStartTime) .toString() .toUpperCase(); //文件进度 // 暂停 const { token, // cancel } = axios.CancelToken.source(); const sourceToken = (file.sourceToken = token); console.log(sourceToken); file.stopUpload = () => { file.isStop = true; this.$forceUpdate(); // cancel(); }; file.startUpload = () => {}; const chunkSize = this.chunkSize; // 文件> 五倍切片大小 不进行切片上传 if (file.size < chunkSize * 1) { this.sendFile(file, sourceToken); } else { const chunkInfo = await this.cutBlob(file, chunkSize); //文件分片 const chunkArr = chunkInfo.chunkArr; this.queueReq({ //递归顺序上传切片文件 file, sourceToken, chunkArr, progressCallback: (chunkItem, data) => { console.log(chunkItem, data); this.progressCallback(file, chunkItem, data); }, endCallBack: () => { this.endCallBack(); file.uploadStatus = "success"; this.$forceUpdate(); }, errorCallBack: (data) => { file.startUpload = () => { const { token, // cancel } = axios.CancelToken.source(); //中断请求 const sourceToken = (file.sourceToken = token); file.isStop = false; file.stopUpload = () => { file.isStop = true; this.$forceUpdate(); // cancel(); }; this.queueReq({ ...data, sourceToken, file }); this.$forceUpdate(); }; this.errorCallBack(data); }, }); } }, cutBlob(file, chunkSize) { const chunkArr = []; // 所有切片缓存数组 const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice; // 切割Api不同浏览器分割处理 const chunkNums = Math.ceil(file.size / chunkSize); // 切片总数 return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsArrayBuffer(file); reader.addEventListener("loadend", () => { let startIndex = ""; let endIndex = ""; let contentItem = ""; // 文件切割分片 for (let i = 0; i < chunkNums; i++) { startIndex = i * chunkSize; endIndex = startIndex + chunkSize; endIndex > file.size && (endIndex = file.size); contentItem = blobSlice.call(file, startIndex, endIndex); chunkArr.push({ index: i, // 用于合并文件 hash: file.hash, // 区分上传 total: chunkNums, // 分片总文件数 name: file.name, // 文件名称 size: file.size, // 文件大小 chunk: contentItem, // 分片文件 }); } resolve({ chunkArr, fileInfo: { hash: file.hash, total: chunkNums, name: file.name, size: file.size, }, }); }); reader.addEventListener("error", function _error(err) { reject(err); }); }); }, queueReq({ file, sourceToken, chunkArr, result, progressCallback, endCallBack, errorCallBack, }) { if (chunkArr.length == 0) { return endCallBack(); } const chunkItem = chunkArr.shift(); let formdata = new FormData(); formdata.append("file", chunkItem.chunk); formdata.append("hash", chunkItem.hash); formdata.append("index", chunkItem.index); formdata.append("name", chunkItem.name); let params = { suffix: chunkItem.name.split(".").pop().toLowerCase(), subscript: chunkItem.index, }; if (result) { // 第二次发起拿到上次请求的返回 params.noGroupPath = result.data.data; } if (file.isStop) { chunkArr.unshift(chunkItem); //移除已经上传的分片 errorCallBack({ errMessage: { message: "暂停" }, chunkItem, sourceToken, chunkArr, result, progressCallback, endCallBack, errorCallBack, }); return; } this.sendChunk({ chunkItem, data: formdata, params, progressCallback, cancelToken: sourceToken, }).then( (res) => { this.queueReq({ file, sourceToken, chunkArr, progressCallback, errorCallBack, endCallBack, result: res, }); }, (err) => { chunkArr.unshift(chunkItem); errorCallBack({ errMessage: err, chunkItem, sourceToken, chunkArr, result, progressCallback, endCallBack, errorCallBack, }); } ); }, sendChunk({ chunkItem, data, params, progressCallback, cancelToken }) { return axios({ url: "/test/upload", method: "post", data, params, headers: { "Content-Type": "multipart/form-data" }, cancelToken, onUploadProgress: (e) => { chunkItem.loaded = e.loaded; progressCallback(chunkItem, e); // 回调 }, }); }, progressCallback(file, chunkItem, data) { // console.log(data, "===="); // 上传中回调 const { loaded, total } = data; file.uploadedChunkSize += loaded < total ? 0 : +loaded; file.uploadedChunkSize > chunkItem.size && (file.uploadedChunkSize = chunkItem.size); file.precent = ((file.uploadedChunkSize / chunkItem.size) * 100).toFixed( 2 ); this.$forceUpdate(); // 刷新视图 }, endCallBack() { console.log("上传完成"); }, errorCallBack({ chunkArr, sourceToken }) { console.log(chunkArr, sourceToken); }, },};</script><style>.pointer { cursor: pointer;}</style>