SpringBoot返回文件让前端下载的几种方式

01 背景

在后端开发中,通常会有文件下载的需求,常用的解决方案有两种:

  1. 不通过后端应用,直接使用nginx直接转发文件地址下载(适用于一些公开的文件,因为这里不需要授权)
  2. 通过后端进行下载,同时进行一些业务处理

本篇主要以方法2进行介绍,方法2的原理步骤如下:

  1. 读取文件,得到文件的字节流
  2. 将字节流写入到响应输出流中
02 一次性读取到内存,通过响应输出流输出到前端
    @GetMapping("/file/download")public void fileDownload(HttpServletResponse response, @RequestParam("filePath") String filePath) {File file = new File(filePath);if (!file.exists()) {throw new BusinessException("当前下载的文件不存在,请检查路径是否正确");}// 将文件写入输入流try (InputStream is = new BufferedInputStream(Files.newInputStream(file.toPath()))) {// 一次性读取到内存中byte[] buffer = new byte[is.available()];int read = is.read(buffer);// 清空 responseresponse.reset();response.setCharacterEncoding("UTF-8");// Content-Disposition的作用:告知浏览器以何种方式显示响应返回的文件,用浏览器打开还是以附件的形式下载到本地保存// attachment表示以附件方式下载   inline表示在线打开   "Content-Disposition: inline; filename=文件名.mp3"// filename表示文件的默认名称,因为网络传输只支持URL编码的相关支付,因此需要将文件名URL编码后进行传输,前端收到后需要反编码才能获取到真正的名称response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));// 告知浏览器文件的大小response.addHeader("Content-Length", "" + file.length());OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());response.setContentType("application/octet-stream");outputStream.write(buffer);outputStream.flush();outputStream.close();} catch (IOException e) {throw new RuntimeException(e);}}

适用于小文件,如果文件过大,一次性读取到内存中可能会出现oom的问题

02 将文件流通过循环写入到响应输出流中(推荐)
    @GetMapping("/file/download")public void fileDownload(HttpServletResponse response, @RequestParam("filePath") String filePath) {File file = new File(filePath);if (!file.exists()) {throw new BusinessException("当前下载的文件不存在,请检查路径是否正确");}// 清空 responseresponse.reset();response.setCharacterEncoding("UTF-8");response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));response.setContentType("application/octet-stream");// 将文件读到输入流中try (InputStream is = new BufferedInputStream(Files.newInputStream(file.toPath()))) {OutputStream outputStream = new BufferedOutputStream(response.getOutputStream());byte[] buffer = new byte[1024];int len;//从输入流中读取一定数量的字节,并将其存储在缓冲区字节数组中,读到末尾返回-1while((len = is.read(buffer)) > 0){outputStream.write(buffer, 0, len);}outputStream.close();} catch (IOException e) {throw new RuntimeException(e);}}
03 从网络上获取文件并返回给前端
    @GetMapping("/net/download")public void netDownload(HttpServletResponse response, @RequestParam("fileAddress") String fileAddress, @RequestParam("filename") String filename) {try {URL url = new URL(fileAddress);URLConnection conn = url.openConnection();InputStream inputStream = conn.getInputStream();response.reset();response.setContentType(conn.getContentType());response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(filename, "UTF-8"));byte[] buffer = new byte[1024];int len;OutputStream outputStream = response.getOutputStream();while ((len = inputStream.read(buffer)) > 0) {outputStream.write(buffer, 0, len);}inputStream.close();} catch (IOException e) {throw new RuntimeException(e);}}
04 从网络上获取文本并下载到本地
    @GetMapping("/netDownloadLocal")public void downloadNet(@RequestParam("netAddress") String netAddress, @RequestParam("filepath") String filepath) {try {URL url = new URL(netAddress);URLConnection conn = url.openConnection();InputStream inputStream = conn.getInputStream();FileOutputStream fileOutputStream = new FileOutputStream(filepath);int byteread;byte[] buffer = new byte[1024];while ((byteread = inputStream.read(buffer)) != -1) {fileOutputStream.write(buffer, 0, byteread);}fileOutputStream.close();} catch (IOException e) {throw new RuntimeException(e);}}
05 总结

一定要搞清楚InputStreamOutputStream的区别,如果搞不清楚的,可以和字符流进行映射,InputStream -> Reader,OutPutStream -> Writer,换成这样你就知道读取内容需要使用Reader,写入需要使用Writer了。

返回给前端的是输出流,不需要你显示的去返回(return response;),这样会报错

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/499159.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

win10、win11-鼠标右键还原、暂停更新

系统优化 win 10jihuo win 11jihuo鼠标右键还原暂停更新 update 2024.12.28win 10 jihuo winx,打开powershell管理员,输入以下命令,选择1并等待 irm https://get.activated.win | iex参考:https://www.bilibili.com/video/BV1TN411M72J/?sp…

【蓝桥杯选拔赛真题87】python输出字符串 第十五届青少年组蓝桥杯python选拔赛真题 算法思维真题解析

目录 python输出字符串 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序编写 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python输出字符串 第十五届蓝桥杯青少年组python比赛选拔赛真题详细解析…

有什么AI辅助阅读文献工具推荐?

AI的发展速度非常快,在很多方面都已经可以提供货真价实的辅助能力。 比如AI辅助阅读方面,可以获取、分析和理解大量文献资料。这可以帮助我们快速浏览和理解PDF文件和其他文档,提高我们的工作效率和学习效率,达到降本增效。 作为…

各个Spring Cloud版本有何主要差异

Spring Cloud 的各个版本之间确实存在一些关键差异,这些差异主要体现在功能更新、性能优化、对新技术的支持以及对旧有技术的替代等方面。 1. Spring Cloud Dalston 这是 Spring Cloud 的一个早期版本,它提供了微服务架构所需的基本组件,如服…

【翻译】审慎对齐:推理使更安全的语言模型成为可能

原文:https://arxiv.org/abs/2412.16339 出自OpenAI 摘要 随着大规模语言模型对安全关键领域的影响越来越大,确保它们可靠地遵守定义良好的原则仍然是一个基本挑战。本文提出慎思校准,一种新的范式,直接教模型安全规范&#xff…

1、ELK的架构和安装

ELK简介 elk:elasticsearch logstash kibana,统一日志收集系统。 elasticsearch:分布式的全文索引引擎的非关系数据库,json格式,在elk中存储所有的日志信息,架构有主和从,最少需要2台。 …

使用连字符容易出错,尽量使用驼峰式的

在<Select>组件中&#xff0c;存在一个拼写错误。在option - value属性中&#xff0c;正确的写法应该是option - value&#xff08;使用连字符&#xff09;或者optionValue&#xff08;如果是按照Vue组件属性的驼峰命名法&#xff09;&#xff0c;这里写成了option - val…

CentOS7 解决ping:www.baidu.com 未知的名称或服务

CentOS7 解决ping&#xff1a;www.baidu.com“未知的名称或服务 在VM查看网络配置 查看虚拟网络编辑器 编辑网络配置文件 vi /etc/sysconfig/network-scripts/ifcfg-ens33注意&#xff1a;不同机器的配置文件名可能不相同&#xff0c;通过 ip addr 命令查看 将 ONBOOT 从 no 改…

QT----------文件系统操作和文件读写

一、输入输出设备类 功能&#xff1a; Qt 提供了一系列的输入输出设备类&#xff0c;用于处理不同类型的 I/O 操作&#xff0c;如文件、网络等。 二、文件读写操作类 QFile 类&#xff1a; 提供了对文件的读写操作&#xff0c;可以打开、读取、写入和关闭文件。示例&#x…

Android14 CTS-R6和GTS-12-R2不能同时测试的解决方法

背景 Android14 CTS r6和GTS 12-r1之后&#xff0c;tf-console默认会带起OLC Server&#xff0c;看起来olc server可能是想适配ATS(android-test-station)&#xff0c;一种网页版可视化、可配置的跑XTS的方式。这种网页版ATS对测试人员是比较友好的&#xff0c;网页上简单配置下…

2024-12-29-sklearn学习(26)模型选择与评估-交叉验证:评估估算器的表现 今夜偏知春气暖,虫声新透绿窗纱。

文章目录 sklearn学习(26) 模型选择与评估-交叉验证&#xff1a;评估估算器的表现26.1 计算交叉验证的指标26.1.1 cross_validate 函数和多度量评估26.1.2 通过交叉验证获取预测 26.2 交叉验证迭代器26.2.1 交叉验证迭代器–循环遍历数据26.2.1.1 K 折26.2.1.2 重复 K-折交叉验…

免费容器镜像服务--统信容器镜像平台

原文链接&#xff1a;免费容器镜像服务--统信容器镜像平台 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于 免费容器镜像服务——统信容器镜像平台 的文章。统信容器镜像平台是由统信软件推出的免费容器镜像服务&#xff0c;遵循 OCI&#xff08;Open Containe…

Vue 3.0 中 template 多个根元素警告问题

在 Vue 2.0 中&#xff0c;template 只允许存在一个根元素&#xff0c;但是这种情况在 Vue 3.0 里发生了一些变化。 在 Vue 3.0 中开始支持 template 存在多个根元素了。但是因为 VSCode 中的一些插件没有及时更新&#xff0c;所以当你在 template 中写入多个根元素时&#xf…

基于JavaWeb的汽车维修保养智能预约系统

作者简介&#xff1a;Java领域优质创作者、CSDN博客专家 、CSDN内容合伙人、掘金特邀作者、阿里云博客专家、51CTO特邀作者、多年架构师设计经验、多年校企合作经验&#xff0c;被多个学校常年聘为校外企业导师&#xff0c;指导学生毕业设计并参与学生毕业答辩指导&#xff0c;…

Kafka 幂等性与事务

文章目录 幂等性实现机制配置使用局限性 事务使用场景配置使用实现机制事务过程事务初始化事务开始事务提交事务取消事务消费 幂等性 Producer 无论向 Broker 发送多少次重复的数据&#xff0c;Broker 端只会持久化一条&#xff0c;保证数据不丢失且不重复。 实现机制 通过引…

ActiveMQ支持哪些传输协议

ActiveMQ 支持多种传输协议&#xff0c;以满足不同场景下的需求。这些协议包括但不限于以下几种&#xff1a; 1. OpenWire&#xff1a; • 这是 ActiveMQ 的默认和专有协议。 • 提供了高效、可靠的消息传递功能。 • 支持多种消息传递模式&#xff0c;如点对点和发布/订阅。 2…

MySQL数据库——常见慢查询优化方式

本文详细介绍MySQL的慢查询相关概念&#xff0c;分析步骤及其优化方案等。 文章目录 什么是慢查询日志&#xff1f;慢查询日志的相关参数如何启用慢查询日志&#xff1f;方式一&#xff1a;修改配置文件方式二&#xff1a;通过命令动态启用 分析慢查询日志方式一&#xff1a;直…

Qt天气预报系统设计界面布局第四部分左边

Qt天气预报系统设计 1、第四部分左边的第一部分1.1添加控件1.2修改控件名字 2、第四部分左边的第二部分2.1添加控件2.2修改控件名字 3、第四部分左边的第三部分3.1添加控件3.2修改控件名字 4、对整个widget04l调整 1、第四部分左边的第一部分 1.1添加控件 拖入一个widget&…

【嵌入式硬件】嵌入式显示屏接口

数字显示串行接口&#xff08;Digital Display Serial Interface&#xff09; SPI 不过多赘述。 I2C-bus interface 不过多赘述 MIPI DSI MIPI (Mobile Industry Processor Interface) Alliance, DSI (Display Serial Interface) 一般用于移动设备&#xff0c;下面是接口…