断点 java,java断点续传下载

  断点 java,java断点续传下载

  00-1010介绍效果的前端代码和后端代码

  00-1010下载超大文件时,如果下载中途暂停,继续下载怎么办?下载是解决这个问题的方法。

  具体原则:

  使用indexedDb,下载的数据存储在用户的本地,这样即使用户关闭电脑,下一次下载也会从上一个位置开始。

  首先,检查本地缓存中是否有该文件的碎片数据。如果有,则从上一个片段(起始位置)继续下载。下载前先去后端获取文件大小,然后计算下载多少次(n/(1024*1024*10))(结束位置)。每个下载的数据都放入一个Blob中,然后存储在本地indexedDB中。当所有下载完成后,合并所有本地缓存的切片,然后告诉用户他们必须使用content-length、Accept-Ranges、Content-Range和Range。但这只是一个前端协议,你不必遵守,只要你和后端就如何获取数据达成一致。

  难点在前端:

  如何存储,如何计算下载次数,如何获取最后下载的片段是什么,如何判断下载完成,如何保证所有下载的片段都是完整的,下载后如何合并再给用户。

  

目录

 

  

介绍

!DOCTYPE html lang= en head meta charset= UTF-8 meta http-equiv= X-UA-Compatible content= IE=edge meta name= viewport content= width=device-widt h,Initial-scale=1.0 title document/title/head body h1 html 5大文件断点下载/h1 div id= progress bar /div button id= button download/button id= stop pause/button script type= module import files liced download from /fileslicedownload . add progress( # progress bar )but . addevent listener( click ,Function(){ fileslicedownload . start download(fileName)})stop . addevent listener( click ,Function(){ filesliddownload . stop()})/script/body/html classblogs {//blob到文件并下载静态downloadFileByBlob(blob,fileName= file ){ let blo burl=window。URL . createobjecturl(blob)let link=document . createelement( a )link . download=fileName Default name link . style . display= none link . href=blob URL//触发单击document . body . append child(link)link . click()//remove

 

   document.body.removeChild(link) }}export default BlobUtls;

//导包要从项目全路径开始,也就是最顶部import BlobUtls from /web-js/src/blob/BlobUtls.js//导包class FileSliceDownload{ #m1=1024*1024*10 //1mb 每次下载多少 #db //indexedDB库对象 #downloadUrl // 下载文件的地址 #fileSizeUrl // 获取文件大小的url #fileSiez=0 //下载的文件大小 #fileName // 下载的文件名称 #databaseName="dbDownload"; //默认库名称 #tableDadaName="tableDada" //用于存储数据的表 #tableInfoName="tableInfo" //用于存储信息的表 #fIleReadCount=0 //文件读取次数 #fIleStartReadCount=0//文件起始的位置 #barId = "bar"; //进度条id #progressId = "progress";//进度数值ID #percent=0 //百分比 #checkDownloadInterval=null; //检测下载是否完成定时器 #mergeInterval=null;//检测是否满足合并分片要求 #stop=false; //是否结束 //下载地址 constructor(downloadUrl,fileSizeUrl) { this.check() this.#downloadUrl=downloadUrl; this.#fileSizeUrl=fileSizeUrl; } check(){ let indexedDB = window.indexedDB window.webkitIndexedDB window.mozIndexedDB ; if(!indexedDB){ alert(不支持); } } //初始化 #init(fileName){ return new Promise((resolve,reject)=>{ this.#fileName=fileName; this.#percent=0; this.#stop=false; const request = window.indexedDB.open(this.#databaseName, 1) request.onupgradeneeded = (e) => { const db = e.target.result if (!db.objectStoreNames.contains(this.#tableDadaName)) { db.createObjectStore(this.#tableDadaName, { keyPath: serial,autoIncrement:false }) db.createObjectStore(this.#tableInfoName, { keyPath: primary,autoIncrement:false }) } } request.onsuccess = e => { this.#db = e.target.result resolve() } }) } #getFileSize(){ return new Promise((resolve,reject)=>{ let ref=this; var xhr = new XMLHttpRequest(); //同步 xhr.open("GET", this.#fileSizeUrl+"/"+this.#fileName,false) xhr.send() if (xhr.readyState === 4 && xhr.status === 200) { let ret = JSON.parse(xhr.response) if (ret.code === 20000) { ref.#fileSiez=ret.data } resolve() } }) } #getTransactionDadaStore(){ let transaction = this.#db.transaction([this.#tableDadaName], readwrite) let store = transaction.objectStore(this.#tableDadaName) return store; } #getTransactionInfoStore(){ let transaction = this.#db.transaction([this.#tableInfoName], readwrite) let store = transaction.objectStore(this.#tableInfoName) return store; } #setBlob(begin,end,i,last){ return new Promise((resolve,reject)=>{ var xhr = new XMLHttpRequest(); xhr.open("GET", this.#downloadUrl+"/"+this.#fileName+"/"+begin+"/"+end+"/"+last) xhr.responseType="blob" // 只支持异步,默认使用 text 作为默认值。 xhr.send() xhr.onload = ()=> { if (xhr.status === 200) { let store= this.#getTransactionDadaStore() let obj={serial:i,blob:xhr.response} //添加分片到用户本地的库中 store.add(obj) let store2= this.#getTransactionInfoStore() //记录下载了多少个分片了 store2.put({primary:"count",count:i}) //调整进度条 let percent1= Math.ceil( (i/this.#fIleReadCount)*100) if(this.#percent<percent1){ this.#percent=percent1; } this.#dynamicProgress() resolve() } } }) } #mergeCallback(){ // 读取全部字节到blob里,处理合并 let arrayBlobs = []; let store1 = this.#getTransactionDadaStore() //按顺序找到全部的分片 for (let i = 0; i <this.#fIleReadCount; i++) { let result= store1.get(IDBKeyRange.only(i)) result.onsuccess=(data)=>{ arrayBlobs.push(data.target.result.blob) } } //分片合并下载 this.#mergeInterval= setInterval(()=> { if(arrayBlobs.length===this.#fIleReadCount){ clearInterval(this.#mergeInterval); //多个Blob进行合并 let fileBlob = new Blob(arrayBlobs);//合并后的数组转成⼀个Blob对象。 BlobUtls.downloadFileByBlob(fileBlob,this.#fileName) //下载完毕后清除数据 this. #clear() } },200) } #clear(){ let store2 = this.#getTransactionDadaStore() let store3 = this.#getTransactionInfoStore() store2.clear() //清除本地全下载的数据 store3.delete("count")//记录清除 this.#fIleStartReadCount=0 //起始位置 this.#db=null; this.#fileName=null; this.#fileSiez=0; this.#fIleReadCount=0 //文件读取次数 this.#fIleStartReadCount=0//文件起始的位置 } //检测是否有分片在本地 #checkSliceDoesIsExist(){ return new Promise((resolve,reject)=>{ let store1 = this.#getTransactionInfoStore() let result= store1.get(IDBKeyRange.only("count")) result.onsuccess=(data)=>{ let count= data.target.result?.count if(count){ //防止因为网络的原因导致分片损坏,所以不要最后一个分片 this.#fIleStartReadCount=count-1; } resolve(); } }) } /** * 样式可以进行修改 * @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) } #dynamicProgress(){ //调整进度 let bar = document.getElementById(this.#barId) let progressEl = document.getElementById(this.#progressId) bar.style.width = this.#percent + %; bar.style.backgroundColor = red; progressEl.innerHTML = this.#percent + % } stop(){ this.#stop=true; } startDownload(fileName){ //同步代码块 ;(async ()=>{ //初始化 await this.#init(fileName) //自动调整分片,如果本地以下载了那么从上一次继续下载 await this.#checkSliceDoesIsExist() //拿到文件的大小 await this.#getFileSize() let begin=0; //开始读取的字节 let end=this.#m1; // 结束读取的字节 let last=false; //是否是最后一次读取 this.#fIleReadCount= Math.ceil( this.#fileSiez/this.#m1) for (let i = this.#fIleStartReadCount; i < this.#fIleReadCount; i++) { if(this.#stop){ return } begin=i*this.#m1; end=begin+this.#m1 if(i===this.#fIleReadCount-1){ last=true; } //添加分片 await this.#setBlob(begin,end,i,last) } //定时检测存下载的分片数量是否够了 this.#checkDownloadInterval= setInterval(()=> { let store = this.#getTransactionDadaStore() let result = store.count() result.onsuccess = (data) => { if (data.target.result === this.#fIleReadCount) { clearInterval(this.#checkDownloadInterval); //如果分片够了那么进行合并下载 this.#mergeCallback() } } },200) })() }}export default FileSliceDownload;

 

  

后端代码

package com.controller.commontools.fileDownload;import com.application.Result;import com.container.ArrayByteUtil;import com.file.FileWebDownLoad;import com.file.ReadWriteFileUtils;import com.path.ResourceFileUtil;import org.springframework.web.bind.annotation.*;import javax.servlet.http.HttpServletResponse;import java.io.BufferedOutputStream;import java.io.File;import java.io.OutputStream;import java.net.URLEncoder;@RestController@RequestMapping("/fileslice")public class FIleSliceDownloadController { private final String uploaddir="uploads"+ File.separator+"real"+File.separator;//实际文件目录 // 获取文件的大小 @GetMapping("/fIleSliceDownloadSize/{fileName}") public Result getFIleSliceDownloadSize(@PathVariable String fileName){ String absoluteFilePath = ResourceFileUtil.getAbsoluteFilePathAndCreate(uploaddir)+File.separator+fileName; File file= new File(absoluteFilePath); if(file.exists()&&file.isFile()){ return Result.Ok(file.length(),Long.class); } return Result.Error(); } /** * 分段下载文件 * @param fileName 文件名称 * @param begin 从文件什么位置开始读取 * @param end 到什么位置结束 * @param last 是否是最后一次读取 * @param response */ @GetMapping("/dwnloadsFIleSlice/{fileName}/{begin}/{end}/{last}") public void dwnloadsFIleSlice(@PathVariable String fileName, @PathVariable long begin, @PathVariable long end, @PathVariable boolean last, HttpServletResponse response){ String absoluteFilePath = ResourceFileUtil.getAbsoluteFilePathAndCreate(uploaddir)+File.separator+fileName; File file= new File(absoluteFilePath); try(OutputStream toClient = new BufferedOutputStream(response.getOutputStream())) { long readSize = end - begin; //读取文件的指定字节 byte[] bytes = new byte[(int)readSize]; ReadWriteFileUtils.randomAccessFileRead(file.getAbsolutePath(),(int)begin,bytes); if(readSize<=file.length()last){ bytes=ArrayByteUtil.getActualBytes(bytes); //去掉多余的 } response.setContentType("application/octet-stream"); response.addHeader("Content-Length", String.valueOf(bytes.length)); response.setHeader("Content-Disposition", "attachment;filename*=UTF-8" + URLEncoder.encode(fileName, "UTF-8")); toClient.write(bytes); } catch (Exception e) { e.printStackTrace(); } }}

以上就是Java实现断点下载功能的示例代码的详细内容,更多关于Java断点下载的资料请关注盛行IT其它相关文章!

 

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: