下载文件,解决oom问题 springboot

背景:一次性将几十兆几百兆的文件读到内存里,然后再传给用户,服务器就爆了。

解决原则:读一点传一点。

解决方法:利用流,循环读写。

使用HttpURLConnection和bufferedInputStream 缓存流的方式来获取下载文件,读取InputStream输入流时,每次读取的大小为5M,不一次性读取完,就可避免内存溢出的情况。

/*** BufferedInputStream 缓存流下载文件* @param downloadUrl* @param path*/
public static void downloadFile(String downloadUrl, String path){InputStream inputStream = null;OutputStream outputStream = null;try {URL url = new URL(downloadUrl);//这里没有使用 封装后的ResponseEntity 就是也是因为这里不适合一次性的拿到结果,放不下content,会造成内存溢出HttpURLConnection connection =(HttpURLConnection) url.openConnection();//使用bufferedInputStream 缓存流的方式来获取下载文件,不然大文件会出现内存溢出的情况inputStream = new BufferedInputStream(connection.getInputStream());File file = new File(path);if (file.exists()) {file.delete();}outputStream = new FileOutputStream(file);//这里也很关键每次读取的大小为5M 不一次性读取完byte[] buffer = new byte[1024 * 1024 * 5];// 5MBint len = 0;while ((len = inputStream.read(buffer)) != -1) {outputStream.write(buffer, 0, len);}connection.disconnect();}catch (Exception e){e.printStackTrace();}finally {IOUtils.closeQuietly(outputStream);IOUtils.closeQuietly(inputStream);}
}

使用RestTemplate流式处理下载大文件,需要先用RequestCallback定义请求头的接收类型application/octet-stream,然后restTemplate进行请求下载时,对响应进行流式处理而不是将其全部加载到内存中。

/*** 文件下载示例:使用restTemplate请求第三方文件服务下载文件** @author Bruce.CH* @since 2023年9月2日*/
@RestController
public class FileController {private static final Logger LOGGER = LoggerFactory.getLogger(FileController.class);/*** 第三方文件服务下载接口*/private static final String DOWNLOAD_URL = "http://127.0.0.1:8080/v1/file/server/download/{fileId}";/*** 注入restTemplate对象*/@Resourceprivate RestTemplate restTemplate;/*** 【方式3】* 请求第三方文件服务下载接口下载文件id指定的文件:字节流直接绑定到响应的输出流中** @param fileId 第三方文件id* @param response 客户端响应*/@GetMapping("/v1/file/download3/{fileId}")public void download3(@PathVariable("fileId") String fileId, HttpServletResponse response) {LOGGER.info("download file:{}", fileId);Map<String, String> uriVariables = new HashMap<>();uriVariables.put("fileId", fileId);ResponseExtractor<Boolean> responseExtractor = clientHttpResponse -> {// 设置响应头,直接用第三方文件服务的响应头HttpHeaders headers = clientHttpResponse.getHeaders();headers.forEach((key, value) -> response.setHeader(key, value.get(0)));// 收到响应输入流即时拷贝写出到响应输出流中: inputStream -> outputStreamStreamUtils.copy(clientHttpResponse.getBody(), response.getOutputStream());return true;};Boolean execute = restTemplate.execute(DOWNLOAD_URL, HttpMethod.GET, null, responseExtractor, uriVariables);LOGGER.info("download file success?{}", execute);}
}

 

流处理

// GET请求
public static void downLargeFileByStream(String url, String savePath){// 对响应进行流式处理而不是将其全部加载到内存中restTemplate.execute(url, HttpMethod.GET, null, response -> {Files.copy(response.getBody(), Paths.get(savePath));return null;}, httpEntity);
}
// POST请求public static void downLargeFileByStream(String url, Map headers, MultiValueMap<String,String> body, String savePath){headers.put("Content-Type","application/x-www-form-urlencoded");HttpHeaders header = new HttpHeaders();header.setAll(headers);HttpEntity<Object> httpEntity = new HttpEntity(body,header);//定义请求头的接收类型,无请求信息时可以设置为 nullRequestCallback requestCallback = restTemplate.httpEntityCallback(httpEntity, null);// RequestCallback requestCallback = request -> request.getHeaders()// .setAccept(Arrays.asList(MediaType.APPLICATION_OCTET_STREAM, MediaType.ALL));// 对响应进行流式处理而不是将其全部加载到内存中restTemplate.execute(url,HttpMethod.POST,requestCallback, response -> {Files.copy(response.getBody(), Paths.get(savePath));return null;}, httpEntity);
}

流处理测试

/*** 下载文件** @return*/
@GetMapping("/test/downFile")
@ResponseBody
public HttpEntity<InputStreamResource> downFile() {//将文件流封装为InputStreamResource对象InputStream inputStream = this.getClass().getResourceAsStream("/1.txt");InputStreamResource inputStreamResource = new InputStreamResource(inputStream);//设置headerMultiValueMap<String, String> headers = new HttpHeaders();headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=1.txt");HttpEntity<InputStreamResource> httpEntity = new HttpEntity<>(inputStreamResource);return httpEntity;
}/*** 调用上面的接口,测试获取文件*/@Test
public void test7() {RestTemplate restTemplate = new RestTemplate();String url = "http://localhost:8080/chat16/test/downFile";/*** 文件比较大的时候,比如好几个G,就不能返回字节数组了,会把内存撑爆,导致OOM* 需要这么玩:* 需要使用execute方法了,这个方法中有个ResponseExtractor类型的参数,* restTemplate拿到结果之后,会回调{@link ResponseExtractor#extractData}这个方法,* 在这个方法中可以拿到响应流,然后进行处理,这个过程就是变读边处理,不会导致内存溢出*/String result = restTemplate.execute(url,HttpMethod.GET,null,new ResponseExtractor<String>() {@Overridepublic String extractData(ClientHttpResponse response) throws IOException {System.out.println("状态:"+response.getStatusCode());System.out.println("头:"+response.getHeaders());//获取响应体流InputStream body = response.getBody();//处理响应体流String content = IOUtils.toString(body, "UTF-8");return content;}}, new HashMap<>());System.out.println(result);
}

 

RestTemplate下载文件的3种实现方式_resttemplate 下载文件-CSDN博客

一文吃透接口调用神器RestTemplate-腾讯云开发者社区-腾讯云

RestTemplate 下载文件 - 掘金 

使用RestTemplate实现跨服务大文件上传,大概2G_resttemplate 上传大文件-CSDN博客 

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

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

相关文章

MySQL如何进行Sql优化

&#xff08;1&#xff09;客户端发送一条查询语句到服务器&#xff1b; &#xff08;2&#xff09;服务器先查询缓存&#xff0c;如果命中缓存&#xff0c;则立即返回存储在缓存中的数据&#xff1b; &#xff08;3&#xff09;未命中缓存后&#xff0c;MySQL通过关键字将SQ…

基于CNN+数据增强+残差网络Resnet50的少样本高准确度猫咪种类识别—深度学习算法应用(含全部工程源码)+数据集+模型(三)

系列文章目录 基于CNN数据增强残差网络Resnet50的少样本高准确度猫咪种类识别—深度学习算法应用(含全部工程源码)数据集模型&#xff08;一&#xff09; 基于CNN数据增强残差网络Resnet50的少样本高准确度猫咪种类识别—深度学习算法应用(含全部工程源码)数据集模型&#xf…

cytoscapejs获取被点击节点位置,并在该节点附近进行双击展示弹窗

获取节点位置 event.target.renderedPosition()其中event是cytoscapejs监听事件中自带的参数 实现 HTML <div id"cy" style"width: 100%; height: 550px; position: relative"><div id"pop-window">进入详情页</div></d…

day33-37-SpringBootV12(整合Spring,SpringMVC,Mybatis,日志,api测试等框架)

ssm spring --> applicationContext.xml配置文件 springmvc --> springmvc.xml配置文件 mybatis —> mybatis-config.xml配置文件 —> springboot优化了之前的框架配置,思想是约定大于配置 一、引言 1.1 初始化配置 为了使用SSM框架去开发&#xff0c;准备SSM…

UDP报文格式详解

✏️✏️✏️各位看官好&#xff0c;今天给大家分享的是 传输层的另外一个重点协议——UDP。 清风的CSDN博客 &#x1f6e9;️&#x1f6e9;️&#x1f6e9;️希望我的文章能对你有所帮助&#xff0c;有不足的地方还请各位看官多多指教&#xff0c;大家一起学习交流&#xff0…

使用GPT开发食堂采购账单

原始系统中&#xff0c;只有采购量和消耗量&#xff0c;需要添加“余”列&#xff0c;并自动计算的余量 具体实现通过查询GPT获得&#xff1a; 提问&#xff1a; 使用antdesign vue的<a-table>组件做一个互动表&#xff0c;每行输入a和b两值&#xff0c;计算cab&#xf…

Gradio入门详细教程

常用的两款AI可视化交互应用比较&#xff1a; Gradio Gradio的优势在于易用性&#xff0c;代码结构相比Streamlit简单&#xff0c;只需简单定义输入和输出接口即可快速构建简单的交互页面&#xff0c;更轻松部署模型。适合场景相对简单&#xff0c;想要快速部署应用的开发者。便…

【算法小技巧】如何判断奇偶

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

FRP 内网穿透工具部署

FRP 介绍 frp 是一个专注于内网穿透的高性能反向代理应用&#xff0c;支持 TCP、UDP、HTTP、HTTPS 等多种协议&#xff0c;且支持 P2P 通信。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。 官方网站&#xff1a;https://gofrp.org/zh-cn/ 项目地…

YOLOv8优化策略:UniRepLKNetBlock 助力检测 | UniRepLKNet,通用感知大内核卷积网络,2023.12

🚀🚀🚀本文改进: UniRepLKNet,通用感知大内核卷积网络,ImageNet-22K预训练,精度 和速度SOTA,ImageNet达到88%, COCO达到56.4 box AP,ADE20K达到55.6 mIoU UniRepLKNetBlock 与C2f进行结合使用 🚀🚀🚀YOLOv8改进专栏:http://t.csdnimg.cn/hGhVK 学姐带你学…

文献阅读(15)Griffin

文章目录 题目&#xff1a;Griffin: Rethinking Sparse Optimization for Deep Learning Architectures时间&#xff1a;2022会议&#xff1a;HPCA研究机构&#xff1a;三星 本篇论文最大的贡献我认为是用统一的表示方法规范表示了各种稀疏计算的类型&#xff0c;并针对不同稀…

The LINQ expression “xxx“ could not be translated

错误示例&#xff1a; var A B .GroupBy(item > item.id) .Select(groupedList > new { PlannerId groupedList.Key, RxList groupedList }); 解决方案&#xff1a; var A B .GroupBy(item > item.id) .Select(groupedList > new { PlannerId groupedList.…

Go实现http同步文件操作 - 增删改查

http同步文件操作 - 增删改查 http同步文件操作 - 增删改查1. 前置要求1.1. 构建结构体 文件名 文件内容1.1.1. 页面结构体1.1.2. 为Page结构体绑定方法&#xff1a;Save1.1.3. 对Page结构体支持页面内容查看方法&#xff0c;同时提供页面文件是否存在的方法 1.2. 简单验证上面…

内存盘imdisk下载链接

使用内存盘&#xff0c;一方面是速度快&#xff0c;另外一方面可以减少硬盘操作。 ImDisk Toolkit download | SourceForge.net ImDisk Toolkit - Browse Files at SourceForge.net

08-1 ⾯向对象基础

⾯向对象基础 ⽬标 理解⾯向对象 类和对象 添加和获取对象属性 魔法⽅法 1.面向对象概述&#xff08;简单理解&#xff09; ⾯向对象是⼀种抽象化的编程思想&#xff0c;很多编程语⾔中都有的⼀种思想。 例如&#xff1a;洗⾐服 思考&#xff1a;⼏种途径可以完成洗⾐…

Axure动态面板的应用与ERP系统登录界面、主页左侧菜单栏、公告栏的绘制

目录 一、动态面板 1.1 简介 1.2 使用动态面板的原因 二、动态面板之轮播图实现案例 2.1 完成步骤 2.2 最终效果 三、动态面版之多方式登录案例 四、动态面板之后台主界面左侧菜单栏 五、ERP登录界面 六、ERP主界面菜单栏 七、ERP公告栏 八、登录页面跳转公告栏 一…

【密码学】群的证明(习题)

0.前置知识 1.习题 记录一次密码学作业~群的判定 2.求解

@SpringBootApplication 包含的三个注解及其含义

一、SpringBootApplication 注解源码 // // Source code recreated from a .class file by IntelliJ IDEA // (powered by Fernflower decompiler) //package org.springframework.boot.autoconfigure;import java.lang.annotation.Documented; import java.lang.annotation.E…

简洁高效的 NLP 入门指南: 200 行实现 Bert 文本分类 (Pytorch 版)

简洁高效的 NLP 入门指南: 100 行实现 Bert 文本分类 Pytorch 版 概述NLP 的不同任务Bert 概述MLM 任务 (Masked Language Modeling)TokenizeMLM 的工作原理为什么使用 MLM NSP 任务 (Next Sentence Prediction)NSP 任务的工作原理NSP 任务栗子NSP 任务的调整和局限性 安装和环…

C#读取Excel中的公式,并生成值

在C#中读取Excel中的公式并生成其计算结果可以使用开源库如EPPlus或Microsoft.Office.Interop.Excel&#xff0c;如果是.xlsm宏文件需用到Microsoft.Office.Interop.Excel。 1.EPPlus方式 using System; using OfficeOpenXml; class Program { static void Main() {…