素材巴巴 > 程序开发 >

vue断点续传一

程序开发 2023-09-05 10:53:10

vue+spring 断点续传(一)

本文主要利用vue和SpringBoot实现了断点续传,大文件分割传递。本章主要讲述前端实现,后端实现请查看 断点续传二


一、断点续传要点

断点续传的核心就是文件分割,对于大文件来说断点续传是十分有必要的,如若直接上传大文件则很有可能会使网络请求时间过长,并且不能保证在上传过程中网络没有波动,因此大文件上传需要使用分片上传,分片的大小需要根据实际情况确定,本文则粗略以5MB计算
在这里插入图片描述

二、前端实现

1.环境

vue2、element-ui2.3.6、 spark-md5 3.0.1

2.html

html主要是运用的element的upload组件和table表格,没有进行任何美化(只进行了一点点美化-_-)


 
.upload-demo {display: flex;flex-direction: column;align-items: center;
 }
 

3.逻辑部分

代码如下(示例):以下代码并没有考虑实际的生产环境,还有很多未完善之处,比如缓存的清除逻辑、并发的控制是否合理等等问题,还请同学们自行完善

    import SparkMD5 from "spark-md5";//  分片大小const shardSize = 1024 * 1024 * 5;//  支持并发数const concurrency = 8;//  并发控制临界const concurrencyLimit = 50;export default {data() {return {fileList: [],list: [],uploadURL: "http://localhost:8888/file/upload/shard",statusURL: "http://localhost:8888/file/status",memoryFileList: "http://localhost:8888/file/list",Headers: {"Content-Type": "application/json; charset=utf-8",},tableData: [],};},created() {this.getMemoryFileInfo();},methods: {//  请求文件上传信息async queryFileInfo(data) {return this.$axios.post(this.statusURL, data, {Headers: this.headers}).then((res) => {return res.data;});},//  上传分片async uploadShard(shard) {return this.$axios.post(this.uploadURL, shard, {Headers: this.headers}).then((res) => {let table = this.tableData;table.forEach((val) => {if (val.hashCode === res.data.fileInfo.hashCode) {val.progress = this.getProgress(res.data.fileInfo.hasUploadShard.length,res.data.fileInfo.totalIndex);}});this.tableData = table;return res.data;});},//分片产生md5getMD5Code(file) {return new Promise((resolve) => {let blobSlice =File.prototype.slice ||File.prototype.mozSlice ||File.prototype.webkitSlice;const chunkSize = 1024 * 1024 * 5;const chunks = Math.ceil(file.size / chunkSize);let currentChunk = 0;let spark = new SparkMD5.ArrayBuffer();let fileReader = new FileReader();fileReader.onload = function (e) {spark.append(e.target.result); // Append array buffercurrentChunk++;if (currentChunk < chunks) {loadNext();} else {resolve(spark.end());}};fileReader.onerror = function () {console.warn("oops, something went wrong.");};function loadNext() {let start = currentChunk * chunkSize;let end =start + chunkSize >= file.size ? file.size : start + chunkSize;fileReader.readAsArrayBuffer(blobSlice.call(file, start, end));}loadNext();});},/**选中的文件 */async fileChange(file) {//  定义常量const fileName = file.name,fileSize = file.size,hashCode = await this.getMD5Code(file.raw),totalIndex = Math.ceil(fileSize / shardSize);const data = new FormData();data.append("fileName", fileName);data.append("fileSize", fileSize);data.append("hashCode", hashCode);data.append("totalIndex", totalIndex);//  存储filethis.saveFile(file, hashCode);//  查询文件状态let fileStatus = await this.queryFileInfo(data);//  更新列表await this.getMemoryFileInfo();//  50MB以下并发传递,50MB以上8个并发传递if (fileStatus.fileInfo.noUploadShard.length * 10 < concurrencyLimit) {//  直接并发传//  根据返回的分片信息上传分片fileStatus.fileInfo.noUploadShard.forEach((index) => {//  检测是否暂停this.tableData.forEach((val) => {if (!val.pauseed) {const shard = this.getShard(file, index);const data2 = new FormData();data2.append("shard", shard);data2.append("hashCode", hashCode);data2.append("shardIndex", index);this.uploadShard(data2);}});});} else {//  8个并发传递//  根据返回的分片信息上传分片let len = fileStatus.fileInfo.noUploadShard.length;let wheel = Math.ceil(len / concurrency);let nowWheel = 0;let loop = setInterval(() => {if (nowWheel < wheel) {let start = nowWheel * concurrency;let end = (nowWheel + 1) * concurrency;for (let i = start; i < end; i++) {//  检测是否暂停this.tableData.forEach((val) => {if (!val.pauseed) {const index = fileStatus.fileInfo.noUploadShard[i];if (index !== undefined) {const shard = this.getShard(file, index);const data2 = new FormData();data2.append("shard", shard);data2.append("hashCode", hashCode);data2.append("shardIndex", index);this.uploadShard(data2);}}});}} else {//  清除定时器退出clearInterval(loop);return;}nowWheel++;}, 100);}},//  获取内存中文件信息async getMemoryFileInfo() {return this.$axios.get(this.memoryFileList, {}, {Headers: this.headers}).then((res) => {//  封装表格数据let arr = [];for (const key in res.data) {let progress = this.getProgress(res.data[key].hasUploadShard.length,res.data[key].totalIndex);let isShow = false;//  是否显示按钮this.list.forEach((val) => {for (const k in val) {if (res.data[key].hashCode === k) {isShow = true;}}});let obj = {fileName: res.data[key].fileName,hashCode: res.data[key].hashCode,progress,pauseed: false,isShow,};arr.push(obj);}this.tableData = arr;});},//  获取进度getProgress(len, total) {return Math.ceil((len / total) * 100);},//  存储filesaveFile(file, hashCode) {let list = this.list;list.push({[hashCode]: file});this.list = list;},//  判断数组是否包含某个元素contains(arr, obj) {let len = arr.length;while (len > 0) {len--;if (arr[len] === obj) {return true;}}return false;},//  根据索引获取分片getShard(file, index) {return file.raw.slice(index * shardSize,Math.min((index + 1) * shardSize, file.size));},//  恢复上传handleUpload(index, row) {this.changeStatus(false, row.hashCode);this.list.forEach((val) => {for (const key in val) {console.log(key);if (row.hashCode === key) {this.fileChange(val[key]);}}});},//  暂停上传handlePause(index, row) {this.changeStatus(true, row.hashCode);},//  改变上传状态changeStatus(boo, hashCode) {let table = this.tableData;table.forEach((val) => {if (val.hashCode === hashCode) {val.pauseed = boo;}});this.tableData = table;},},};
 

总结

加油噢!


标签:

素材巴巴 Copyright © 2013-2021 http://www.sucaibaba.com/. Some Rights Reserved. 备案号:备案中。