SpringBoot返回文件让前端下载的几种方式
01 背景
在后端开发中,通常会有文件下载的需求,常用的解决方案有两种:
- 不通过后端应用,直接使用
nginx直接转发文件地址下载(适用于一些公开的文件,因为这里不需要授权) - 通过后端进行下载,同时进行一些业务处理
本篇主要以方法2进行介绍,方法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 总结
一定要搞清楚InputStream和OutputStream的区别,如果搞不清楚的,可以和字符流进行映射,InputStream -> Reader,OutPutStream -> Writer,换成这样你就知道读取内容需要使用Reader,写入需要使用Writer了。
返回给前端的是输出流,不需要你显示的去返回(return response;),这样会报错
相关文章:
SpringBoot返回文件让前端下载的几种方式
01 背景 在后端开发中,通常会有文件下载的需求,常用的解决方案有两种: 不通过后端应用,直接使用nginx直接转发文件地址下载(适用于一些公开的文件,因为这里不需要授权)通过后端进行下载&#…...
人工智能及深度学习的一些题目
1、一个含有2个隐藏层的多层感知机(MLP),神经元个数都为20,输入和输出节点分别由8和5个节点,这个网络有多少权重值? 答:在MLP中,权重是连接神经元的参数,每个连接都有一…...
15-利用dubbo远程服务调用
本文介绍利用apache dubbo调用远程服务的开发过程,其中利用zookeeper作为注册中心。关于zookeeper的环境搭建,可以参考我的另一篇博文:14-zookeeper环境搭建。 0、环境 jdk:1.8zookeeper:3.8.4dubbo:2.7.…...
【Rust自学】8.5. HashMap Pt.1:HashMap的定义、创建、合并与访问
8.5.0. 本章内容 第八章主要讲的是Rust中常见的集合。Rust中提供了很多集合类型的数据结构,这些集合可以包含很多值。但是第八章所讲的集合与数组和元组有所不同。 第八章中的集合是存储在堆内存上而非栈内存上的,这也意味着这些集合的数据大小无需在编…...
未来网络技术的新征程:5G、物联网与边缘计算(10/10)
一、5G 网络:引领未来通信新潮流 (一)5G 网络的特点 高速率:5G 依托良好技术架构,提供更高的网络速度,峰值要求不低于 20Gb/s,下载速度最高达 10Gbps。相比 4G 网络,5G 的基站速度…...
LLM(十二)| DeepSeek-V3 技术报告深度解读——开源模型的巅峰之作
近年来,大型语言模型(LLMs)的发展突飞猛进,逐步缩小了与通用人工智能(AGI)的差距。DeepSeek-AI 团队最新发布的 DeepSeek-V3,作为一款强大的混合专家模型(Mixture-of-Experts, MoE&a…...
Uniapp在浏览器拉起导航
Uniapp在浏览器拉起导航 最近涉及到要在浏览器中拉起导航,对目标点进行路线规划等功能,踩了一些坑,找到了使用方法。(浏览器拉起) 效果展示 可以拉起三大平台及苹果导航 点击选中某个导航,会携带经纬度跳转…...
公平联邦学习——多目标优化
前言 前段时间接触到了联邦学习(Federated Learning, FL)。涉猎了几年多目标优化的我,惊奇地发现横向联邦学习里面也有用多目标优化来做的。于是有感而发,特此写一篇博客记录记录,如有机会可以和大家多多交流。遇到不…...
奇怪的Python:为何字符串要设置成不可变的?
你好!我是老邓。今天我们来聊聊 Python 中字符串不可变这个话题。 1、问题简介: Python 中,字符串属于不可变对象。这意味着一旦字符串被创建,它的值就无法被修改。任何看似修改字符串的操作,实际上都是创建了一个新…...
Vue-Router之嵌套路由
在路由配置中,配置children import Vue from vue import VueRouter from vue-routerVue.use(VueRouter)const router new VueRouter({mode: history,base: import.meta.env.BASE_URL,routes: [{path: /,redirect: /home},{path: /home,name: home,component: () &…...
MyBatis使用的设计模式
目录 1. 工厂模式(Factory Pattern) 2. 单例模式(Singleton Pattern) 3. 代理模式(Proxy Pattern) 4. 装饰器模式(Decorator Pattern) 5. 观察者模式(Observer Patt…...
arm rk3588 升级glibc2.31到2.33
一、查看glibc版本 rootztl:~# ldd --version ldd (Ubuntu GLIBC 2.31-0ubuntu9.2) 2.31 Copyright (C) 2020 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNE…...
【Linux系列】sed命令的深入解析:如何使用sed删除文件内容
💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…...
C++ 设计模式:桥接模式(Bridge Pattern)
链接:C 设计模式 链接:C 设计模式 - 装饰模式 桥接模式(Bridge Pattern)是一种结构型设计模式,它通过将抽象部分(业务功能)与实现部分(平台实现)分离,使它们…...
MATLAB中whitespacePattern函数用法
目录 语法 说明 示例 匹配空白字符 替换非标准空白 更正错误的间距 whitespacePattern函数的功能是匹配空白字符。 语法 pat whitespacePattern pat whitespacePattern(N) pat whitespacePattern(minCharacters,maxCharacters) 说明 pat whitespacePattern 创建一…...
Django多字段认证的实现
Django多字段认证 需求: django认证的检查用户是username,如果使用 username和 手机号验证登录。 重写: ModelBackend 类下的 authenticate 方法 # 在对应应用下创建 utils.py""" 修改Django认证类,为了实现 …...
【AndroidAPP】权限被拒绝:[android.permission.READ_EXTERNAL_STORAGE],USB设备访问权限系统报错
一、问题原因 1.安卓安全性变更 Android 12 的安全性变更,Google 引入了更严格的 PendingIntent 安全管理,强制要求开发者明确指定 PendingIntent 的可变性(Mutable)或不可变性(Immutable)。 但是…...
SQL进阶技巧:如何分析连续签到领金币数问题?
目录 0 题目需求 1 数据准备 2 问题分析 2.1 代码实现 2.2 代码功能分析 第一段 SQL...
1、ELK的架构和安装
ELK简介 elk:elasticsearch logstash kibana,统一日志收集系统。 elasticsearch:分布式的全文索引引擎的非关系数据库,json格式,在elk中存储所有的日志信息,架构有主和从,最少需要2台。 …...
Vue2/Vue3使用DataV
Vue2 注意vue2与3安装DataV命令命令是不同的Vue3 DataV - Vue3 官网地址 注意vue2与3安装DataV命令命令是不同的 vue3vite 与 Vue3webpack 对应安装也不同vue3vite npm install kjgl77/datav-vue3全局引入 // main.ts中全局引入 import { createApp } from vue import Da…...
Docker化部署KingbaseES V9:从镜像导入到开发版License激活实战
1. 为什么选择Docker部署KingbaseES V9? 在开发测试环境中,传统数据库安装方式往往需要耗费大量时间在环境配置和依赖解决上。我去年参与的一个政务云项目就遇到过这种情况:团队花了三天时间在不同操作系统的测试机上反复折腾依赖库ÿ…...
JLink V9.5 固件资源包
JLink V9.5 固件资源包 【下载地址】JLinkV9.5固件资源包 JLink V9.5 固件资源包欢迎使用JLink V9.5全套固件资源 项目地址: https://gitcode.com/open-source-toolkit/4bb56 欢迎使用JLink V9.5全套固件资源。本资源包专为那些需要对JLink调试器进行固件升级和自定义配…...
复古CRT电视改造:用RF调制器连接树莓派与现代电脑
1. 项目概述:当太空时代美学遇见现代计算几年前,我在一个复古科技展上第一次见到JVC Videosphere,那个圆润的球面屏幕和未来感十足的造型瞬间击中了我。它诞生于上世纪70年代,是那个太空竞赛黄金时期工业设计的缩影。但和大多数老…...
Multi-head Self-Attention Machanism
3. 多头自注意力机制(Multi-head Self-Attention Machanism) 多头注意力机制是在自注意力机制的基础上发展起来的,是自注意力机制的变体,旨在增强模型的表达能力和泛化能力。它通过使用多个独立的注意力头,分别计算注…...
【深度解析】Hermes Agent 0.14:OpenAI 兼容本地代理、按需依赖加载与 AI Coding 工作流升级
摘要 Hermes Agent 0.14 是一次偏“基础设施”的重要更新:安装更简单、启动更轻量,并引入 OpenAI 兼容本地代理能力,使其更适合作为模型订阅、代码工具与本地工作流之间的 Agent 路由层。背景介绍 在 AI Coding 生态中,开发者常常…...
发动机悬架系统场景下的多目标优化算法与最优控制算法【附程序】
✨ 长期致力于深度神经网络、深度学习、多目标优化算法、最优控制、主动悬置系统研究工作,擅长数据搜集与处理、建模仿真、程序编写、仿真设计。 ✅ 专业定制毕设、代码 ✅ 如需沟通交流,点击《获取方式》 (1)基于深度神经网络与N…...
WindowResizer终极指南:5分钟掌握Windows窗口强制调整技巧
WindowResizer终极指南:5分钟掌握Windows窗口强制调整技巧 【免费下载链接】WindowResizer 一个可以强制调整应用程序窗口大小的工具 项目地址: https://gitcode.com/gh_mirrors/wi/WindowResizer 还在为那些顽固的Windows应用程序窗口而烦恼吗?无…...
嵌入式C函数指针覆盖变量问题分析与解决方案
1. 函数指针覆盖变量问题解析在嵌入式C语言开发中,函数指针是一种强大的工具,但也可能带来一些难以察觉的问题。特别是在Keil MDK等嵌入式开发环境中,函数指针的错误使用可能导致变量被意外覆盖,这类问题往往难以调试。1.1 问题现…...
NotebookLM思维导图生成已进入「语义拓扑时代」:2024Q2最新Benchmark显示其节点关联准确率超越MindNode Pro 41.6%
更多请点击: https://intelliparadigm.com 第一章:NotebookLM思维导图生成已进入「语义拓扑时代」 传统基于关键词共现或规则模板的思维导图生成方式,正被 NotebookLM 的语义理解能力彻底重构。其底层 LLM 模型不再仅识别显式术语关系&#…...
别再死磕PSO了!用Python手把手教你实现GWO灰狼优化算法(附完整代码)
用Python实战GWO灰狼优化算法:告别传统优化方法的局限 在工程优化和机器学习领域,算法选择往往决定了问题求解的效率和质量。传统粒子群优化(PSO)算法虽然广为人知,但其参数调节复杂、易陷入局部最优的缺点也日益明显。灰狼优化算法(Grey Wol…...
