- JDK版本: 1.8
- Hutool版本: 5.8.25
客服端文件上传主要代码:
大文件上传 java.lang.OutOfMemoryError: Java heap space
从异常堆栈信息中可以看出这里使用了 java.io.ByteArrayOutputStream 。实际上就是把文件全部都加载到了Byte数组中,如果上传的文件过大必定会导致OOM。
hutool Request执行流程
这里实际上是使用的 java.net.HttpURLConnection。
java.net.HttpURLConnection 是支持 StreamingMode 传输HTTP请求的,有两种方式开启:
- setFixedLengthStreamingMode
当预先知道内容长度时,该方法用于使得能够在没有内部缓冲的情况下流式传输HTTP请求主体。
如果应用程序尝试写入比指示的content-length更多的数据,或者如果应用程序在写入指示的数量之前关闭OutputStream,则将引发异常。 - setChunkedStreamingMode
当内容长度为不提前知道。在这种模式下,使用分块传输编码来发送请求正文。请注意,并非所有HTTP服务器都支持此模式。
启用输出流时,无法自动处理身份验证和重定向。如果需要身份验证或重定向,则读取响应时将引发HttpRetryException。
Hutool 的 HttpRequest中只提供了 setChunkedStreamingMode方式,setFixedLengthStreamingMode 方式其实感觉上会更好,不会出现服务端不支持的情况,作者表示下一版本中将会支持setFixedLengthStreamingMode 。
先来测试一下 setChunkedStreamingMode 的效果。
这里自己写一个服务端的接口看看StreamingMode的header有什么区别。
这是修改前会出现OOM的客户端代码
堆内存明显增高
服务端日志输出:
客户端上传日志:
客户端通过 setChunkedStreamingMode 开启 StreamingMode:
上传文件时堆内存无明细变化:
服务端日志输出:
客户端上传日志:
正常上传请求包含 header, 来告诉服务端当前请求主体内容的字节数。
中 没有 ,而是新增了 。
-
Transfer-Encoding: chunked:
- 这是一种 HTTP 传输编码,允许服务器在不知道整个响应内容长度的情况下,分批次发送数据。
- 每个数据块前会有一个指定大小的头部,表明该块的大小,直到遇到大小为 的块,表示传输结束。
-
服务端处理:
- 服务端(如 Tomcat)在接收到 编码的请求时,会按照分块传输编码的规则来读取数据。
- 服务端会持续读取数据块,直到检测到一个大小为 的块,这表示输入流已经结束。
-
Tomcat 配置:
- Tomcat 允许通过配置 标签的 属性来限制请求体的最大大小。
- 参数定义了上传文件写入磁盘的阈值,这对于处理大文件上传尤为重要。
-
流式上传:
- Tomcat 支持流式上传,这意味着数据可以边读边写,不需要将整个文件内容一次性加载到内存中。
- 流式上传适用于大文件或实时数据传输,如视频流。
-
异步处理:
- Tomcat 支持 Servlet 3.0 规范中的异步处理机制,允许长时间运行的操作在单独的线程中执行。
- 这可以提高 Tomcat 的并发处理能力和系统吞吐量。
-
异常处理:
- 在文件上传过程中,如果出现异常(如文件大小超出限制),Tomcat 会抛出相应的异常。
- 开发者需要在代码中妥善处理这些异常,并在必要时进行异常捕获和处理。
-
请求结束:
- 处理完所有数据块后,Tomcat 会关闭输入流,并根据请求的内容执行相应的业务逻辑。
用了这么久HTTP, 你是否了解Content-Length和Transfer-Encoding
用了这么久HTTP, 你是否了解Content-Length和Transfer-Encoding
HTTP响应字段Transfer-Encoding含义及作用详解