通过Java实现文件断点续传功能

您所在的位置:网站首页 文件断点续传实现什么功能 通过Java实现文件断点续传功能

通过Java实现文件断点续传功能

2024-07-12 10:48| 来源: 网络整理| 查看: 265

用户上传大文件,网络差点的需要历时数小时,万一线路中断,不具备断点续传的服务器就只能从头重传,而断点续传就是,允许用户从上传断线的地方继续传送,这样大大减少了用户的烦恼。本文将用Java语言实现断点续传,需要的可以参考一下

什么是断点续传 用户上传大文件,网络差点的需要历时数小时,万一线路中断,不具备断点续传的服务器就只能从头重传,而断点续传就是,允许用户从上传断线的地方继续传送,这样大大减少了用户的烦恼。

解决上传大文件服务器内存不够的问题 解决如果因为其他因素导致上传终止的问题,并且刷新浏览器后仍然能够续传,重启浏览器(关闭浏览器后再打开)仍然能够继续上传,重启电脑后仍然能够上传 检测上传过程中因网络波动导致文件出现了内容丢失那么需要自动检测并且从新上传

解决方案 前端

需要进行分割上传的文件 需要对上传的分片文件进行指定文件序号 需要监控上传进度,控制进度条 上传完毕后需要发送合并请求 Blob 对象,操作文件

后端

上传分片的接口 合并分片的接口 获取分片的接口 其他工具方法,用于辅助 前端端需要注意的就是: 文件的切割,和进度条

后端需要注意的就是: 分片存储的地方和如何进行合并分片

效果演示 先找到需要上传的文件 在这里插入图片描述 当我们开始上传进度条就会发生变化,当我们点击停止上传那么进度条就会停止 在这里插入图片描述 我们后端会通过文件名+文件大小进行MD5生成对应的目录结果如下: 在这里插入图片描述 当前端上传文件达到100%时候就会发送文件合并请求,然后我们后端这些分片都将被合并成一个文件 在这里插入图片描述 通过下图可以看到所有分片都没有了,从而合并出来一个文件 在这里插入图片描述 以上就是断点续传的核心原理,但是还需处理一些异常情况:

文件上传过程中网络波动导致流丢失一部分(比对大小) 文件上传过程中,服务器丢失分片 (比对分片的连续度) 文件被篡改内容(比对大小) 效验核心代 在这里插入图片描述 参考代码

前端

Document html5大文件断点切割上传 import FileSliceUpload from '../jsutils/FileSliceUpload.js' let testingUrl="http://localhost:7003/fileslice/testing" let uploadUrl="http://localhost:7003/fileslice/uploads" let margeUrl="http://localhost:7003/fileslice/merge-file-slice" let progressUrl="http://localhost:7003/fileslice/progress" let fileSliceUpload= new FileSliceUpload(testingUrl,uploadUrl,margeUrl,progressUrl,"#file") fileSliceUpload.addProgress("#progressBar") let btn= document.querySelector("#btn") let btn1= document.querySelector("#btn1") btn.addEventListener("click",function () { fileSliceUpload.startUploadFile() }) btn1.addEventListener("click",function () { fileSliceUpload.stopUploadFile() }) //大文件分片上传,比如10G的压缩包,或者视频等,这些文件太大了 (需要后端配合进行) class FileSliceUpload{ constructor(testingUrl, uploadUrl, margeUrl,progressUrl, fileSelect) { this.testingUrl = testingUrl; // 检测文件上传的url this.uploadUrl = uploadUrl;//文件上传接口 this.margeUrl = margeUrl; // 合并文件接口 this.progressUrl = progressUrl; //进度接口 this.fileSelect = fileSelect; this.fileObj = null; this.totalize = null; this.blockSize = 1024 * 1024; //每次上传多少字节1mb(最佳) this.sta = 0; //起始位置 this.end = this.sta + this.blockSize; //结束位置 this.count = 0; //分片个数 this.barId = "bar"; //进度条id this.progressId = "progress";//进度数值ID this.fileSliceName = ""; //分片文件名称 this.fileName = ""; this.uploadFileInterval = null; //上传文件定时器 } /** * 样式可以进行修改 * @param {*} progressId 需要将进度条添加到那个元素下面 */ addProgress (progressSelect) { let bar = document.createElement("div") bar.setAttribute("id", this.barId); let num = document.createElement("div") num.setAttribute("id", this.progressId); num.innerText = "0%" bar.appendChild(num); document.querySelector(progressSelect).appendChild(bar) } //续传 在上传前先去服务器检测之前是否有上传过这个文件,如果还有返回上传的的分片,那么进行续传 // 将当前服务器上传的最后一个分片会从新上传, 避免因为网络的原因导致分片损坏 sequelFile () { if (this.fileName) { var xhr = new XMLHttpRequest(); //同步 xhr.open('GET', this.testingUrl + "/" + this.fileName+ "/" + this.blockSize+ "/" + this.totalize, false); xhr.send(); if (xhr.readyState === 4 && xhr.status === 200) { let ret = JSON.parse(xhr.response) if (ret.code == 20000) { let data= ret.data this.count = data.code; this.fileSliceName = data.fileSliceName //计算起始位置和结束位置 this.sta = this.blockSize * this.count //计算结束位置 this.end = this.sta + this.blockSize } else { this.sta = 0; //从头开始 this.end = this.sta + this.blockSize; this.count = 0; //分片个数 } } } } stopUploadFile () { clearInterval(this.uploadFileInterval) } // 文件上传(单文件) startUploadFile () { // 进度条 let bar = document.getElementById(this.barId) let progressEl = document.getElementById(this.progressId) this.fileObj = document.querySelector(this.fileSelect).files[0]; this.totalize = this.fileObj.size; this.fileName = this.fileObj.name; //查询是否存在之前上传过此文件,然后继续 this.sequelFile() let ref = this; //拿到当前对象的引用,因为是在异步中使用this就是他本身而不是class this.uploadFileInterval = setInterval(function () { if (ref.sta > ref.totalize) { //上传完毕后结束定时器 clearInterval(ref.uploadFileInterval) //发送合并请求 ref.margeUploadFile () console.log("stop" + ref.sta); return; }; //分片名称 ref.fileSliceName = ref.fileName + "-slice-" + ref.count++ //分割文件 , var blob1 = ref.fileObj.slice(ref.sta, ref.end); var fd = new FormData(); fd.append('part', blob1); fd.append('fileSliceName', ref.fileSliceName); fd.append('fileSize', ref.totalize); var xhr = new XMLHttpRequest(); xhr.open('POST', ref.uploadUrl, true); xhr.send(fd); //异步发送文件,不管是否成功, 会定期检测 xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { let ret = JSON.parse(xhr.response) if (ret.code == 20000) { //计算进度 let percent = Math.ceil((ret.data*ref.blockSize/ ref.totalize) * 100) if (percent > 100) { percent=100 } bar.style.width = percent + '%'; bar.style.backgroundColor = 'red'; progressEl.innerHTML = percent + '%' } } } //起始位置等于上次上传的结束位置 ref.sta = ref.end; //结束位置等于上次上传的结束位置+每次上传的字节 ref.end = ref.sta + ref.blockSize; }, 5) } margeUploadFile () { console.log("检测上传的文件完整性.........."); var xhr = new XMLHttpRequest(); //文件分片的名称/分片大小/总大小 xhr.open('GET', this.margeUrl+ "/" + this.fileSliceName + "/" + this.blockSize + "/" + this.totalize, true); xhr.send(); //发送请求 xhr.onreadystatechange = function () { if (xhr.readyState === 4 && xhr.status === 200) { let ret = JSON.parse(xhr.response) if (ret.code == 20000) { console.log("文件上传完毕"); } else { console.log("上传完毕但是文件上传过程中出现了异常", ret); } } } } } export default FileSliceUpload;

后端 因为代码内部使用较多自己封装的工具类的原因,以下代码只提供原理的参考

package com.controller.commontools.fIleupload; import com.alibaba.fastjson.JSON; import com.application.Result; import com.container.ArrayByteUtil; import com.encryption.hash.HashUtil; import com.file.FileUtils; import com.file.FileWebUpload; import com.file.ReadWriteFileUtils; import com.function.impl.ExecutorUtils; import com.path.ResourceFileUtil; import com.string.PatternCommon; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import java.util.stream.Collectors; @RestController @RequestMapping("/fileslice") public class FIleSliceUploadController { private final String identification="-slice-"; private final String uploadslicedir="uploads"+File.separator+"slice"+File.separator;//分片目录 private final String uploaddir="uploads"+File.separator+"real"+File.separator;//实际文件目录 //获取分片 @GetMapping("/testing/{fileName}/{fileSlicSize}/{fileSize}") public Result testing(@PathVariable String fileName,@PathVariable long fileSlicSize,@PathVariable long fileSize ) throws Exception { String dir = fileNameMd5Dir(fileName,fileSize); String absoluteFilePathAndCreate = ResourceFileUtil.getAbsoluteFilePathAndCreate(uploadslicedir)+File.separator+dir; File file = new File(absoluteFilePathAndCreate); if (file.exists()) { List filesAll = FileUtils.getFilesAll(file.getAbsolutePath()); if (filesAll.size()fileSliceSize?fileSliceSize:readFileSize*(finalI+1); i1


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3