新用户注册入口 老用户登录入口

[转载]SpringBoot+Vue 实现大文件断点下载

文章作者:转载 更新时间:2023-01-19 08:12:45 阅读数量:545
文章标签:文件切片下载分片传输断点续传Vue前端
本文摘要:本文详细介绍了在SpringBoot项目中利用HttpServletResponse和HttpServletRequest实现文件切片下载功能,通过处理Range请求头实现断点续传。后端Java代码展示了如何根据用户输入的文件名读取文件流,并使用InputStream、OutputStream进行文件分片传输。前端部分采用Vue框架展示交互界面,用户可以暂停与开始下载操作,以提升大文件下载体验。关键词:SpringBoot、HttpServletResponse、HttpServletRequest、InputStream、OutputStream、文件切片下载、分片传输、断点续传、Range请求头、Vue前端。
转载文章

本篇文章为转载内容。原文链接:https://blog.csdn.net/kangshihang1998/article/details/129407214。

该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。

作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。

如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。

目录

  • 效果
  • 代码
    • SpringBoot
    • Vue

效果

步骤

  1. 点击下载 在输入框输入下载的文件名称
  2. 点击暂停
  3. 再次点击开始

image-20230308164210026

image-20230308164315589

下载完成

image-20230308164418834

image-20230308164532862

代码

SpringBoot

pom

     <!-- 做断点下载使用--><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpcore</artifactId></dependency><dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.8.0</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>

controller

package com.kang.controller;import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Optional;/*** @Description 文件切片下载* @ClassName DownLoadController* @Author 康世行* @Date 20:58 2023/2/22* @Version 1.0**/
@Controller
@Slf4j
public class DownLoadController {private final static String utf8 = "utf-8";@RequestMapping("/down")public void downLoadFile(HttpServletRequest request, HttpServletResponse response) throws IOException {// 设置编码格式response.setCharacterEncoding(utf8);//获取文件路径String fileName=request.getParameter("fileName");String drive=request.getParameter("drive");//参数校验log.info(fileName,drive);//完整路径(路径拼接待优化-前端传输优化-后端从新格式化  )String pathAll=drive+":\\"+fileName;log.info("pathAll{}",pathAll);Optional<String> pathFlag = Optional.ofNullable(pathAll);File file=null;if (pathFlag.isPresent()){//根据文件名,读取file流file = new File(pathAll);log.info("文件路径是{}",pathAll);if (!file.exists()){log.warn("文件不存在");return;} }else {//请输入文件名log.warn("请输入文件名!");return;}InputStream is = null;OutputStream os = null;try {//分片下载long fSize = file.length();//获取长度response.setContentType("application/x-download");String file_Name = URLEncoder.encode(file.getName(),"UTF-8");response.addHeader("Content-Disposition","attachment;filename="+fileName);//根据前端传来的Range  判断支不支持分片下载response.setHeader("Accept-Range","bytes");//获取文件大小//response.setHeader("fSize",String.valueOf(fSize));response.setHeader("fName",file_Name);//定义断点long pos = 0,last = fSize-1,sum = 0;//判断前端需不需要分片下载if (null != request.getHeader("Range")){response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);String numRange = request.getHeader("Range").replaceAll("bytes=","");String[] strRange = numRange.split("-");if (strRange.length == 2){pos = Long.parseLong(strRange[0].trim());last = Long.parseLong(strRange[1].trim());//若结束字节超出文件大小 取文件大小if (last>fSize-1){last = fSize-1;} }else {//若只给一个长度  开始位置一直到结束pos = Long.parseLong(numRange.replaceAll("-","").trim());} }long rangeLenght = last-pos+1;String contentRange = new StringBuffer("bytes").append(pos).append("-").append(last).append("/").append(fSize).toString();response.setHeader("Content-Range",contentRange);// response.setHeader("Content-Lenght",String.valueOf(rangeLenght));os = new BufferedOutputStream(response.getOutputStream());is = new BufferedInputStream(new FileInputStream(file));is.skip(pos);//跳过已读的文件(重点,跳过之前已经读过的文件)byte[] buffer = new byte[1024];int lenght = 0;//相等证明读完while (sum < rangeLenght){lenght = is.read(buffer,0, (rangeLenght-sum)<=buffer.length? (int) (rangeLenght - sum) :buffer.length);sum = sum+lenght;os.write(buffer,0,lenght);}log.info("下载完成");}finally {if (is!= null){is.close();}if (os!=null){os.close();} }} }

启动成功
在这里插入图片描述

Vue


<html xmlns:th="http://www.thymeleaf.org"><head><meta charset="utf-8"/><title>狂神说Java-ES仿京东实战</title><link rel="stylesheet" th:href="@{/css/style.css}"/>
</head><body class="pg">
<div class="page" id="app"><div id="mallPage" class=" mallist tmall- page-not-market "><!-- 头部搜索 --><div id="header" class=" header-list-app"><div class="headerLayout"><div class="headerCon "><!-- Logo--><h1 id="mallLogo"><img th:src="@{/images/jdlogo.png}" alt=""></h1><div class="header-extra"><!--搜索--><div id="mallSearch" class="mall-search"><form name="searchTop" class="mallSearch-form clearfix"><fieldset><legend>天猫搜索</legend><div class="mallSearch-input clearfix"><div class="s-combobox" id="s-combobox-685"><div class="s-combobox-input-wrap"><input v-model="keyword" type="text" autocomplete="off" value="java" id="mq"class="s-combobox-input" aria-haspopup="true"></div></div><button type="submit" @click.prevent="searchKey" id="searchbtn">搜索</button></div></fieldset></form><ul class="relKeyTop"><li><a>狂神说Java</a></li><li><a>狂神说前端</a></li><li><a>狂神说Linux</a></li><li><a>狂神说大数据</a></li><li><a>狂神聊理财</a></li></ul></div></div></div></div></div><el-button @click="download" id="download">下载</el-button>
<!--        <el-button @click="concurrenceDownload" >并发下载测试</el-button>--><el-button @click="stop">停止</el-button><el-button @click="start">开始</el-button>{{fileFinalOffset} }{{contentList} }<el-progress type="circle" :percentage="percentage"></el-progress></div><!--前端使用Vue,实现前后端分离--><script th:src="@{/js/axios.min.js}"></script><script th:src="@{/js/vue.min.js}"></script><!-- 引入样式 -->
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"><!-- 引入组件库 -->
<script src="https://unpkg.com/element-ui/lib/index.js"></script><script>new Vue({  el: '#app',data: {keyword: '', //搜索关键字results: [] ,//搜索结果percentage: 0, // 下载进度filesCurrentPage:0,//文件开始偏移量fileFinalOffset:0, //文件最后偏移量stopRecursiveTags:true, //停止递归标签,默认是true 继续进行递归contentList: [], // 文件流数组breakpointResumeTags:false, //断点续传标签,默认是false 不进行断点续传temp:[],fileMap:new Map(),timer:null, //定时器名称},methods: {//根据关键字搜索商品信息searchKey(){var keyword=this.keyword;axios.get('/search/JD/search/'+keyword+"/1/10").then(res=>{this.results=res.data;//绑定数据console.log(this.results)console.table(this.results)})},//停止下载stop(){//改变递归标签为falsethis.stopRecursiveTags=false;},//开始下载start(){//重置递归标签为true 最后进行合并this.stopRecursiveTags=true;//重置断点续传标签this.breakpointResumeTags=true;//重新调用下载方法this.download();},// 分段下载需要后端配合download() {// 下载地址const url = "/down?fileName="+this.keyword.trim()+"&drive=E";console.log(url)const chunkSize = 1024 * 1024 *50; // 单个分段大小,这里测试用100Mlet filesTotalSize = chunkSize; // 安装包总大小,默认100Mlet filesPages = 1; // 总共分几段下载//计算百分比之前先清空上次的if(this.percentage==100){this.percentage=0;}let sentAxios = (num) => {let rande = chunkSize;//判断是否开启了断点续传(断点续传没法并行-需要上次请求的结果作为参数)if (this.breakpointResumeTags){rande = `${Number(this.fileFinalOffset)+1}-${num * chunkSize + 1}`;}else {if (num) {rande = `${(num - 1) * chunkSize + 2}-${num * chunkSize + 1}`;} else {// 第一次0-1方便获取总数,计算下载进度,每段下载字节范围区间rande = "0-1";} }let headers = {range: rande,};axios({method: "get",url: url.trim(),async: true,data: {},headers: headers,responseType: "blob"}).then((response) => {if (response.status == 200 || response.status == 206) {//检查了下才发现,后端对文件流做了一层封装,所以将content指向response.data即可const content = response.data;//截取文件总长度和最后偏移量let result= response.headers["content-range"].split("/");// 获取文件总大小,方便计算下载百分比filesTotalSize =result[1];//获取最后一片文件位置,用于断点续传this.fileFinalOffset=result[0].split("-")[1]// 计算总共页数,向上取整filesPages = Math.ceil(filesTotalSize / chunkSize);// 文件流数组this.contentList.push(content);// 递归获取文件数据(判断是否要继续递归)if (this.filesCurrentPage < filesPages&&this.stopRecursiveTags==true) {this.filesCurrentPage++;//计算下载百分比  当前下载的片数/总片数this.percentage=Number((this.contentList.length/filesPages)*100).toFixed(2);sentAxios(this.filesCurrentPage);//结束递归return;}//递归标签为true 才进行下载if (this.stopRecursiveTags){// 文件名称const fileName =decodeURIComponent(response.headers["fname"]);//构造一个blob对象来处理数据const blob = new Blob(this.contentList);//对于<a>标签,只有 Firefox 和 Chrome(内核) 支持 download 属性//IE10以上支持blob但是依然不支持downloadif ("download" in document.createElement("a")) {//支持a标签download的浏览器const link = document.createElement("a"); //创建a标签link.download = fileName; //a标签添加属性link.style.display = "none";link.href = URL.createObjectURL(blob);document.body.appendChild(link);link.click(); //执行下载URL.revokeObjectURL(link.href); //释放urldocument.body.removeChild(link); //释放标签} else {//其他浏览器navigator.msSaveBlob(blob, fileName);} }} else {//调用暂停方法,记录当前下载位置console.log("下载失败")} }).catch(function (error) {console.log(error);});};// 第一次获取数据方便获取总数sentAxios(this.filesCurrentPage);this.$message({message: '文件开始下载!',type: 'success'});} }})</script>
</body>
</html>

本篇文章为转载内容。原文链接:https://blog.csdn.net/kangshihang1998/article/details/129407214。

该文由互联网用户投稿提供,文中观点代表作者本人意见,并不代表本站的立场。

作为信息平台,本站仅提供文章转载服务,并不拥有其所有权,也不对文章内容的真实性、准确性和合法性承担责任。

如发现本文存在侵权、违法、违规或事实不符的情况,请及时联系我们,我们将第一时间进行核实并删除相应内容。

相关阅读
文章标题:[转载][洛谷P1082]同余方程

更新时间:2023-02-18
[转载][洛谷P1082]同余方程
文章标题:[转载]webpack优化之HappyPack实战

更新时间:2023-08-07
[转载]webpack优化之HappyPack实战
文章标题:[转载]oracle 同时更新多表,在Oracle数据库中同时更新两张表的简单方法

更新时间:2023-09-10
[转载]oracle 同时更新多表,在Oracle数据库中同时更新两张表的简单方法
文章标题:[转载][Unity] 包括场景互动与射击要素的俯视角闯关游戏Demo

更新时间:2024-03-11
[转载][Unity] 包括场景互动与射击要素的俯视角闯关游戏Demo
文章标题:[转载]程序员也分三六九等?等级差异,一个看不起一个!

更新时间:2024-05-10
[转载]程序员也分三六九等?等级差异,一个看不起一个!
文章标题:[转载]海贼王 动漫 全集目录 分章节 精彩打斗剧集

更新时间:2024-01-12
[转载]海贼王 动漫 全集目录 分章节 精彩打斗剧集
名词解释
作为当前文章的名词解释,仅对当前文章有效。
文件切片下载文件切片下载是一种网络传输技术,通过将大文件分割成多个较小的数据块(或称“切片”),然后独立地逐个进行下载。在本文中,SpringBoot后端实现的文件下载功能支持文件切片,允许用户暂停和继续下载过程。当用户请求下载文件时,服务器会根据HTTP请求头中的Range信息,返回特定范围内的文件数据块,从而实现断点续传,即即使在网络不稳定导致下载中断的情况下,客户端仍能从上次停止的地方继续下载剩余的部分。
断点续传断点续传是网络文件传输过程中的一种重要特性,特别是在大文件下载场景中尤为实用。它允许用户在文件下载过程中因网络问题或其他原因暂时中断后,能够在恢复连接或再次尝试下载时,从之前中断的位置(即断点)继续下载,而无需重新开始整个文件的下载。在文章所展示的SpringBoot代码示例中,通过处理HTTP Range请求头来实现这一功能。
HTTP请求头RangeHTTP协议中的Range请求头用于客户端向服务器请求资源的一部分,而不是整个资源。在文件下载场景下,Range头可以指定一个或多个字节范围,服务器收到这样的请求后,只返回请求范围内对应的文件内容。这使得客户端能够实现文件的分片下载与断点续传。在该篇文章介绍的SpringBoot控制器中,就利用了Range请求头的信息来判断并执行文件的切片下载操作。
延伸阅读
作为当前文章的延伸阅读,仅对当前文章有效。
在了解了如何在SpringBoot项目中结合Vue实现文件切片下载功能之后,我们可以进一步探索这一技术在现代Web应用开发中的实际应用场景和最新趋势。
近日,随着云计算、大数据以及5G网络的普及,大文件传输与高效下载的需求愈发显著。例如,某知名云存储服务提供商近期宣布升级其文件下载服务,采用先进的分段传输技术以应对用户对超大文件快速稳定下载的需求,这正是基于类似上述文章所介绍的HTTP Range请求头原理。通过服务器端的智能分片处理和客户端的断点续传支持,极大提升了用户在各种网络环境下的下载体验。
此外,前端技术社区也在不断优化大文件下载的用户体验。有开发者分享了一篇关于如何利用Vue.js配合WebSocket实现实时下载进度展示的文章,其中详细解读了在进行文件分片下载时,如何从前端角度实时获取并更新下载进度信息,从而提升用户界面的互动性和友好性。
对于深入理解文件切片下载机制,推荐阅读《HTTP协议权威指南》一书,书中详尽剖析了HTTP协议中的范围请求(Range Request)及其实现方式,这对于掌握和优化文件下载功能具有极高的参考价值。同时,关注Spring Boot官方文档和社区讨论,可以及时获取到针对大文件处理的最新最佳实践和技术动态。
综上所述,在当前高速发展的互联网环境下,结合SpringBoot后端框架与Vue前端技术实现文件切片下载功能,并关注该领域的最新发展与应用案例,无疑将有助于我们更好地服务于用户的实际需求,提升产品竞争力。
知识学习
实践的时候请根据实际情况谨慎操作。
随机学习一条linux命令:
nohup command & - 使命令在后台持续运行,即使退出终端也不停止。
随便看看
拉到页底了吧,随便看看还有哪些文章你可能感兴趣。
HessianRPC在高负载下服务降级与熔断器模式保障用户体验 05-01 jQuery和TweenMax简单实用的水平手风琴特效 01-20 jquery选择国家下拉列表框插件 01-21 Sqoop在Hadoop集群中的数据传输机制及数据库迁移、收集与备份恢复应用实践 12-23 简约渔具批发牧渔企业类网站前端模板下载 11-09 基于bootstrap功能齐全的jQuery进度条插件 10-20 简约大气男性护肤产品HTML5网站模板 09-22 宽屏大气机械设备制造公司网站模板 08-13 演讲会门票销售网站模板下载 07-30 本次刷新还10个文章未展示,点击 更多查看。
经典响应式投资理财企业前端模板 06-26 基于Redis的键值对存储实现用户阅读状态跟踪与管理 06-24 Netty框架中CannotFindServerSelection异常:服务器地址配置错误与通道类型匹配详解 06-18 简洁设计公司响应式网站模板下载 05-06 绿色苗木草坪种植绿化类企业前端CMS模板下载 04-30 怎么在cmd开启mysql服务 04-15 保洁公司家庭保洁服务网站模板 03-26 SpringCloud微服务中分布式锁的死锁问题与状态一致性维护:避免循环依赖、公平锁及超时重试机制在Redisson中的实践运用 03-19 HBase性能测试与RegionServer配置、架构及数据模型调优实践:关注响应时间、并发处理能力与BlockCache优化 03-14 jquery控制radio触发事件 02-15 简约HTML5软件营销业务公司网站模板 02-09
时光飞逝
"流光容易把人抛,红了樱桃,绿了芭蕉。"