处理Hutool的Http工具上传大文件报OOM

程序环境

  • JDK版本: 1.8
  • Hutool版本: 5.8.25

问题描述

客服端文件上传主要代码:

HttpRequest httpRequest = HttpUtil.createPost(FILE_UPLOAD_URL);
Resource urlResource = new UrlResource(url, fileName);
httpRequest.form("file", urlResource);
HttpResponse httpResponse = httpRequest.execute();

大文件上传 java.lang.OutOfMemoryError: Java heap space

java.lang.OutOfMemoryError: Java heap spaceat java.util.Arrays.copyOf(Arrays.java:3236) ~[na:1.8.0_275]at java.io.ByteArrayOutputStream.grow(ByteArrayOutputStream.java:118) ~[na:1.8.0_275]at java.io.ByteArrayOutputStream.ensureCapacity(ByteArrayOutputStream.java:93) ~[na:1.8.0_275]at java.io.ByteArrayOutputStream.write(ByteArrayOutputStream.java:135) ~[na:1.8.0_275]at sun.net.www.http.PosterOutputStream.write(PosterOutputStream.java:63) ~[na:1.8.0_275]at cn.hutool.http.MultipartOutputStream.write(MultipartOutputStream.java:108) ~[hutool-all-5.8.25.jar!/:5.8.25]at java.io.OutputStream.write(OutputStream.java:116) ~[na:1.8.0_275]at cn.hutool.core.io.copy.StreamCopier.doCopy(StreamCopier.java:102) ~[hutool-all-5.8.25.jar!/:5.8.25]at cn.hutool.core.io.copy.StreamCopier.copy(StreamCopier.java:68) ~[hutool-all-5.8.25.jar!/:5.8.25]at cn.hutool.core.io.IoUtil.copy(IoUtil.java:162) ~[hutool-all-5.8.25.jar!/:5.8.25]at cn.hutool.core.io.IoUtil.copy(IoUtil.java:146) ~[hutool-all-5.8.25.jar!/:5.8.25]at cn.hutool.core.io.IoUtil.copy(IoUtil.java:132) ~[hutool-all-5.8.25.jar!/:5.8.25]at cn.hutool.core.io.IoUtil.copy(IoUtil.java:119) ~[hutool-all-5.8.25.jar!/:5.8.25]at cn.hutool.core.io.resource.Resource.writeTo(Resource.java:76) ~[hutool-all-5.8.25.jar!/:5.8.25]at cn.hutool.http.MultipartOutputStream.appendResource(MultipartOutputStream.java:163) ~[hutool-all-5.8.25.jar!/:5.8.25]at cn.hutool.http.MultipartOutputStream.write(MultipartOutputStream.java:96) ~[hutool-all-5.8.25.jar!/:5.8.25]at cn.hutool.http.body.MultipartBody$$Lambda$2190/568941495.accept(Unknown Source) ~[na:na]at cn.hutool.core.map.TableMap.forEach(TableMap.java:253) ~[hutool-all-5.8.25.jar!/:5.8.25]at cn.hutool.http.body.MultipartBody.write(MultipartBody.java:78) ~[hutool-all-5.8.25.jar!/:5.8.25]at cn.hutool.http.body.RequestBody.writeClose(RequestBody.java:27) ~[hutool-all-5.8.25.jar!/:5.8.25]at cn.hutool.http.HttpRequest.sendMultipart(HttpRequest.java:1402) ~[hutool-all-5.8.25.jar!/:5.8.25]at cn.hutool.http.HttpRequest.send(HttpRequest.java:1340) ~[hutool-all-5.8.25.jar!/:5.8.25]at cn.hutool.http.HttpRequest.doExecute(HttpRequest.java:1188) ~[hutool-all-5.8.25.jar!/:5.8.25]at cn.hutool.http.HttpRequest.execute(HttpRequest.java:1051) ~[hutool-all-5.8.25.jar!/:5.8.25]at cn.hutool.http.HttpRequest.execute(HttpRequest.java:1027) ~[hutool-all-5.8.25.jar!/:5.8.25]at com.mbzj.ai.third.RhzClient.execute(RhzClient.java:270) ~[classes!/:1.0-SNAPSHOT]at com.mbzj.ai.third.RhzClient.uploadKnowledgeFile(RhzClient.java:184) ~[classes!/:1.0-SNAPSHOT]at com.mbzj.ai.third.RhzService.uploadKnowledgeFile(RhzService.java:132) ~[classes!/:1.0-SNAPSHOT]at com.mbzj.ai.listener.KnowledgeFileListener.handleAddKnowledgeFileEvent(KnowledgeFileListener.java:64) ~[classes!/:1.0-SNAPSHOT]at com.mbzj.ai.listener.KnowledgeFileListener$$FastClassBySpringCGLIB$$beafef7e.invoke(<generated>) ~[classes!/:1.0-SNAPSHOT]at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.3.28.jar!/:5.3.28]at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) ~[spring-aop-5.3.28.jar!/:5.3.28]

分析问题

从异常堆栈信息中可以看出这里使用了 java.io.ByteArrayOutputStream 。实际上就是把文件全部都加载到了Byte数组中,如果上传的文件过大必定会导致OOM。

hutool httpRequest执行流程

在这里插入图片描述
这里实际上是使用的 java.net.HttpURLConnection

解决方案

java.net.HttpURLConnection 是支持 StreamingMode 传输HTTP请求的,有两种方式开启:

  • setFixedLengthStreamingMode
    当预先知道内容长度时,该方法用于使得能够在没有内部缓冲的情况下流式传输HTTP请求主体。
    如果应用程序尝试写入比指示的content-length更多的数据,或者如果应用程序在写入指示的数量之前关闭OutputStream,则将引发异常。
  • setChunkedStreamingMode
    当内容长度为不提前知道。在这种模式下,使用分块传输编码来发送请求正文。请注意,并非所有HTTP服务器都支持此模式。
    启用输出流时,无法自动处理身份验证和重定向。如果需要身份验证或重定向,则读取响应时将引发HttpRetryException。

Hutool 的 HttpRequest中只提供了 setChunkedStreamingMode方式,setFixedLengthStreamingMode 方式其实感觉上会更好,不会出现服务端不支持的情况,作者表示下一版本中将会支持setFixedLengthStreamingMode

先来测试一下 setChunkedStreamingMode 的效果。

这里自己写一个服务端的接口看看StreamingMode的header有什么区别。

@PostMapping("test")
public void test(MultipartFile file, HttpServletRequest request) {System.out.println("fileSize" + file.getSize());// 打印所有headerEnumeration<String> headerNames = request.getHeaderNames();while (headerNames.hasMoreElements()) {String name = headerNames.nextElement();System.out.println(name + ":" + request.getHeader(name));}
}

这是修改前会出现OOM的客户端代码

HttpRequest httpRequest = HttpUtil.createPost("http://127.0.0.1:8064/test");
URL fileUrl = new URL("https://xxxx/1a67c727f8a845dd8b0b9825026349dd.mp4");
UrlResource urlResource = new UrlResource(fileUrl, "test.mp4");
httpRequest.form("file", urlResource);
System.out.println(httpRequest);
HttpResponse httpResponse = httpRequest.execute();
System.out.println(httpResponse);

堆内存明显增高
在这里插入图片描述

服务端日志输出:

accept:text/html,application/json,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
user-agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 Hutool
accept-encoding:gzip, deflate
content-type:multipart/form-data; boundary=--------------------Hutool_rV0KKNQCkTkwywrQ
cache-control:no-cache
pragma:no-cache
host:127.0.0.1:8064
connection:keep-alive
content-length:128553150

客户端上传日志:

Request Url: http://127.0.0.1:8064/ai/knowledge/test
Request Headers: Accept: text/html,application/json,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 HutoolAccept-Encoding: gzip, deflate
Request Body: file=https%3A%2F%2Fcos-uclass.lconrise.cn%2Fbiz%2Fai%2Fknowledge%2Ffile%2F1a67c727f8a845dd8b0b9825026349dd.mp4Response Headers: Keep-Alive=[timeout=60]X-Frame-Options=[DENY]null=[HTTP/1.1 200]Cache-Control=[no-cache, no-store, max-age=0, must-revalidate]X-Content-Type-Options=[nosniff]Connection=[keep-alive]Expires=[0]Pragma=[no-cache]Content-Length=[0]X-XSS-Protection=[1; mode=block]Date=[Wed, 11 Sep 2024 01:59:55 GMT]
Response Body: 

客户端通过 setChunkedStreamingMode 开启 StreamingMode:

HttpRequest httpRequest = HttpUtil.createPost("http://127.0.0.1:8064/ai/knowledge/test");
URL fileUrl = new URL("https://cos-uclass.lconrise.cn/biz/ai/knowledge/file/1a67c727f8a845dd8b0b9825026349dd.mp4");
UrlResource urlResource = new UrlResource(fileUrl, "test.mp4");
httpRequest.form("file", urlResource);
httpRequest.setChunkedStreamingMode(1024 * 8);
System.out.println(httpRequest);
HttpResponse httpResponse = httpRequest.execute();
System.out.println(httpResponse);

上传文件时堆内存无明细变化:
在这里插入图片描述

服务端日志输出:

accept:text/html,application/json,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
user-agent:Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 Hutool
accept-encoding:gzip, deflate
content-type:multipart/form-data; boundary=--------------------Hutool_Zn5eac5m74pQH1IJ
cache-control:no-cache
pragma:no-cache
host:127.0.0.1:8064
connection:keep-alive
transfer-encoding:chunked

客户端上传日志:

Request Url: http://127.0.0.1:8064/ai/knowledge/test
Request Headers: Accept: text/html,application/json,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36 HutoolAccept-Encoding: gzip, deflate
Request Body: file=https%3A%2F%2Fcos-uclass.lconrise.cn%2Fbiz%2Fai%2Fknowledge%2Ffile%2F1a67c727f8a845dd8b0b9825026349dd.mp4Response Headers: Keep-Alive=[timeout=60]X-Frame-Options=[DENY]null=[HTTP/1.1 200]Cache-Control=[no-cache, no-store, max-age=0, must-revalidate]X-Content-Type-Options=[nosniff]Connection=[keep-alive]Expires=[0]Pragma=[no-cache]Content-Length=[0]X-XSS-Protection=[1; mode=block]Date=[Wed, 11 Sep 2024 02:02:28 GMT]
Response Body: 

正常上传请求包含 content-lengt header, 来告诉服务端当前请求主体内容的字节数。
StreamingMode 中 没有 content-length ,而是新增了 transfer-encoding:chunked

扩展

  1. Transfer-Encoding: chunked

    • 这是一种 HTTP 传输编码,允许服务器在不知道整个响应内容长度的情况下,分批次发送数据。
    • 每个数据块前会有一个指定大小的头部,表明该块的大小,直到遇到大小为 0 的块,表示传输结束。
  2. 服务端处理

    • 服务端(如 Tomcat)在接收到 chunked 编码的请求时,会按照分块传输编码的规则来读取数据。
    • 服务端会持续读取数据块,直到检测到一个大小为 0 的块,这表示输入流已经结束。
  3. Tomcat 配置

    • Tomcat 允许通过配置 <Connector> 标签的 maxPostSize 属性来限制请求体的最大大小。
    • fileSizeThreshold 参数定义了上传文件写入磁盘的阈值,这对于处理大文件上传尤为重要。
  4. 流式上传

    • Tomcat 支持流式上传,这意味着数据可以边读边写,不需要将整个文件内容一次性加载到内存中。
    • 流式上传适用于大文件或实时数据传输,如视频流。
  5. 异步处理

    • Tomcat 支持 Servlet 3.0 规范中的异步处理机制,允许长时间运行的操作在单独的线程中执行。
    • 这可以提高 Tomcat 的并发处理能力和系统吞吐量。
  6. 异常处理

    • 在文件上传过程中,如果出现异常(如文件大小超出限制),Tomcat 会抛出相应的异常。
    • 开发者需要在代码中妥善处理这些异常,并在必要时进行异常捕获和处理。
  7. 请求结束

    • 处理完所有数据块后,Tomcat 会关闭输入流,并根据请求的内容执行相应的业务逻辑。

用了这么久HTTP, 你是否了解Content-Length和Transfer-Encoding

用了这么久HTTP, 你是否了解Content-Length和Transfer-Encoding

HTTP响应字段Transfer-Encoding含义及作用详解

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

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

相关文章

self-supervised learning(BERT和GPT)

1芝麻街与NLP模型 我們接下來要講的主題呢叫做Self-Supervised Learning&#xff0c;在講self-supervised learning之前呢&#xff0c;就不能不介紹一下芝麻街&#xff0c;為什麼呢因為不知道為什麼self-supervised learning的模型都是以芝麻街的人物命名。 因為Bert是一個非常…

实战-任意文件下载

实战-任意文件下载 1、开局 开局一个弱口令&#xff0c;正常来讲我们一般是弱口令或者sql&#xff0c;或者未授权 那么这次运气比较好&#xff0c;直接弱口令进去了 直接访问看看有没有功能点&#xff0c;正常做测试我们一定要先找功能点 发现一个文件上传点&#xff0c;不…

中酱集团:黑松露酱油,天然配方定义健康生活

在如今的大健康时代&#xff0c;人们对于美食的要求越来越高。不仅美味&#xff0c;更要健康。在健康美食的生态链中&#xff0c;有一个名字正逐渐成为品质与美味的代名词——中酱集团。而当中酱集团与黑松露酱油相遇&#xff0c;一场味觉的革命就此拉开帷幕。 中酱集团&#x…

【产品应用】旋转式贴标机一站式解决方案

针对贴标机行业设备&#xff0c;立迈胜公司拥有智能控制器、一体化伺服电机、减速机等系列产品&#xff0c;可以轻松解决传统电机接线不便、占用空间、自重过大、部件繁杂等问题&#xff0c;帮助贴标机制造商实现设备精准控制、运行稳定的同时&#xff0c;保证生产流程高效产出…

开发运维警示录-20241024

开发警示录 1、作为开发&#xff0c;不要私自修改业务人员给的SQL语句&#xff0c;虽然个人感觉SQL很冗余&#xff0c;效率低等。 2、开发前&#xff0c;要明确需求&#xff0c;必要时通过图和文字形成文档与需求方确认、留痕。 3、开发复杂的业务逻辑代码前&#xff0c;先疏通…

oracle数据库---PL/SQL、存储函数、存储过程、触发器、定时器job、备份

PL/SQL 什么是 PL/SQL PL/SQL&#xff08;Procedure Language/SQL&#xff09;是 Oracle 对 sql 语言的过程化扩展&#xff0c;指在 SQL 命令语言中增加了过程处理语句&#xff08;如分支、循环等&#xff09;&#xff0c;使 SQL 语言具有过程处理能力。把SQL语言的数据操纵能…

瑞芯微的 展会总结

首先是我感兴趣的产品&#xff1a; 摄像头的 墨水瓶的。 android 盒子&#xff0c;使用的是rk3588s 然后是瑞芯微&#xff21;&#xff29;在做什么&#xff1a;  在对 音频 视屏的输出 进行补充。 比如&#xff0c;视频拍了一张图片很模糊&#xff0c;那么他们用AI算法&am…

基于Multisim红外接近报警电路设计(含仿真和报告)

【全套资料.zip】红外接近报警电路设计Multisim仿真设计数字电子技术 文章目录 功能一、Multisim仿真源文件二、原理文档报告资料下载【Multisim仿真报告讲解视频.zip】 功能 标题&#xff1a;红外接近报警电路 红外报警器是当前利用电子技术制作而成的防盗报警器&#xff0c…

Sei 生态迎首个 MMORPG 游戏伙伴 Final Glory,开启新篇章

​“随着 Final Glory 拓展至 SEI Network&#xff0c;SEI 生态也迎来了首款 MMORPG 游戏” 链游赛道新贵 Final Glory Final Glory 是建立在 MateArena 引擎上的 MMORPG 游戏&#xff0c;作为目前行业内首个斥巨资打造的 AAA 级 MMORPG 全链游戏&#xff0c;在面向市场后即引发…

PostgreSQL两节点用keepalived实现主备的高可用架构

使用keepalived实现PostgreSQL数据库两节点主备的高可用架构部署详解 环境配置和规划部署PostgreSQL的主备流复制架构keepalived介绍安装部署keepalived数据库配置配置keepalived相关参数文件启动keepalived模拟故障切换问题记录实践建议 看腻了就来听听视频演示吧&#xff08;…

Java 多线程(八)—— 锁策略,synchronized 的优化,JVM 与编译器的锁优化,ReentrantLock,CAS

前言 本文为 Java 面试小八股&#xff0c;一句话&#xff0c;理解性记忆&#xff0c;不能理解就死背吧。 锁策略 悲观锁与乐观锁 悲观锁和乐观锁是锁的特性&#xff0c;并不是特指某个具体的锁。 我们知道在多线程中&#xff0c;锁是会被竞争的&#xff0c;悲观锁就是指锁…

LSTM反向传播及公式推导

先回顾一下正向传播的公式: 化简一下: 反向传播从下到上逐步求偏导: 对zt求偏导(预测值和标签值相减): zt对未知数wt,ht,bt分别求偏导: ht对ot,Ct求偏导: ot对Net0求偏导: Net0对w0,b0求偏导: .... 总体的思路就是那个公式从下到上逐步对未知数求偏导: 下面是总体的流程…

Flutter项目打包ios, Xcode 发布报错 Module‘flutter barcode_scanner‘not found

报错图片 背景 flutter 开发的 apple app 需要发布新版本&#xff0c;但是最后一哆嗦碰到个报错&#xff0c;这个小问题卡住了我一天&#xff0c;之间的埪就不说了&#xff0c;直接说我是怎么解决的&#xff0c;满满干货 思路 这个报错 涉及到 flutter_barcode_scanner; 所…

微信小程序性能优化 ==== 合理使用 setData 纯数据字段

目录 1. setData 的流程 2. 数据通信 3. 使用建议 3.1 data 应只包括渲染相关的数据 3.2 控制 setData 的频率 3.3 选择合适的 setData 范围 3.4 setData 应只传发生变化的数据 3.5 控制后台态页面的 setData 纯数据字段 组件数据中的纯数据字段 组件属性中的纯数据…

Java.6--多态-设计模式-抽象父类-抽象方法

一、多态 1.定义--什么是多态&#xff1f; a.同一个父类的不同子类对象&#xff0c;在做同一行为的时候&#xff0c;有不同的表现形式&#xff0c;这就是多态。&#xff08;总结为&#xff1a;一个父类下的不同子类&#xff0c;同一行为&#xff0c;不同表现形式。&#xff0…

springboot3.x.x 集成 连接SQL Server 2008 驱动版本和SSL套接字问题的解决

驱动程序无法通过使用安全套接字层(SSL)加密与 SQL Server 建立安全连接。错误:“The server selected protocol version TLS10 is not accepted by client 依赖版本 <dependency><groupId>com.microsoft.sqlserver</groupId><artifactId>mssql-jdbc&…

ABAP 函数

1、基础语句注意事项 1.1基础 SE38编辑 SM30 数据库表中添加多条数据 SE91编辑消息类 SE11查看数据库表 SE16N主要查看数据 1.2语句 1.FOR ALL ENTRIES IN 对于不能使用join的聚集表或者需要使用SELECT 的内表&#xff0c;我们一般使用for all entries in 语句将该表…

虚拟机安装麒麟v10、配置网络、安装docker

一、虚拟机安装麒麟v10 1、下载iso&#xff08;https://www.kylinos.cn/support/trial.html&#xff09; 2、虚拟机安装 3、选择 4、设置开机自动连接网络 参考地址&#xff1a;https://www.cnblogs.com/goding/p/18283912 安装好后发现屏幕分辨率毕竟低&#xff0c;点设置分…

开源模型应用落地-LangChain实用小技巧-带阈值的相似性搜索(十五)

一、前言 带阈值的相似性搜索是一种非常实用的信息检索方法。它允许用户设定一个具体的相似度标准&#xff0c;从而提升搜索结果的相关性和准确性。在面对大规模数据时&#xff0c;传统的相似性搜索往往难以满足用户的需求&#xff0c;因为返回的结果可能包含很多不相干的信息。…

数字图像处理的概念(一)

一 何谓数字图像处理 1 图像的概念 图像是对客观存在的物体的一种相似性的、生动的写真或描述。 2 图像的类别 可见光成像和不可见光成像 单波段、多波段和超波段图像 伽马射线成像 主要用途包括核 医学和天文观测 等 。 核医学 a)同位素注射 骨骼扫描图像 b)正电子放射( …