当前位置: 首页 > news >正文

HttpURLConnection OOM问题记录

使用HttpURLConnection 上传大文件,会出现内存溢出问题:

观察HttpURLConnection 源码:

@Overridepublic synchronized OutputStream getOutputStream() throws IOException {connecting = true;SocketPermission p = URLtoSocketPermission(this.url);if (p != null) {try {return AccessController.doPrivilegedWithCombiner(new PrivilegedExceptionAction<>() {public OutputStream run() throws IOException {return getOutputStream0();}}, null, p            );} catch (PrivilegedActionException e) {throw (IOException) e.getException();}} else {return getOutputStream0();}
}

private synchronized OutputStream getOutputStream0() throws IOException {try {if (!doOutput) {throw new ProtocolException("cannot write to a URLConnection"                           + " if doOutput=false - call setDoOutput(true)");}if (method.equals("GET")) {method = "POST"; // Backward compatibility        }if ("TRACE".equals(method) && "http".equals(url.getProtocol())) {throw new ProtocolException("HTTP method TRACE" +" doesn't support output");}// if there's already an input stream open, throw an exception        if (inputStream != null) {throw new ProtocolException("Cannot write output after reading input.");}if (!checkReuseConnection())connect();boolean expectContinue = false;String expects = requests.findValue("Expect");if ("100-Continue".equalsIgnoreCase(expects) && streaming()) {http.setIgnoreContinue(false);expectContinue = true;}if (streaming() && strOutputStream == null) {writeRequests();}if (expectContinue) {expect100Continue();}ps = (PrintStream)http.getOutputStream();if (streaming()) {if (strOutputStream == null) {if (chunkLength != -1) { /* chunked */                     strOutputStream = new StreamingOutputStream(new ChunkedOutputStream(ps, chunkLength), -1L);} else { /* must be fixed content length */                    long length = 0L;if (fixedContentLengthLong != -1) {length = fixedContentLengthLong;} else if (fixedContentLength != -1) {length = fixedContentLength;}strOutputStream = new StreamingOutputStream(ps, length);}}return strOutputStream;} else {if (poster == null) {poster = new PosterOutputStream();}return poster;}} catch (RuntimeException e) {disconnectInternal();throw e;} catch (ProtocolException e) {// Save the response code which may have been set while enforcing        // the 100-continue. disconnectInternal() forces it to -1        int i = responseCode;disconnectInternal();responseCode = i;throw e;} catch (IOException e) {disconnectInternal();throw e;}
}

public boolean streaming () {return (fixedContentLength != -1) || (fixedContentLengthLong != -1) ||(chunkLength != -1);
}

如上, 默认设置情况下streaming ()  为false。

package sun.net.www.http
public class PosterOutputStream extends ByteArrayOutputStream {
}

PosterOutputStream  默认为 ByteArrayOutputStream  子类

解决办法:

目标服务支持情况下,可以不使用HttpURLConnection的ByteArrayOutputStream缓存机制,直接将流提交到服务器上。如下函数设置:

httpConnection.setChunkedStreamingMode(0); // 或者设置自定义大小,0默认大小

    public void setChunkedStreamingMode (int chunklen) {if (connected) {throw new IllegalStateException ("Can't set streaming mode: already connected");}if (fixedContentLength != -1 || fixedContentLengthLong != -1) {throw new IllegalStateException ("Fixed length streaming mode set");}chunkLength = chunklen <=0? DEFAULT_CHUNK_SIZE : chunklen;}

遗憾的是,我上传的服务不支持这种模式。因此采用固定大小。

HttpURLConnection con = (HttpURLConnection)new URL("url").openConnection();
con.setFixedLengthStreamingMode(输出流的固定长度);
  /*** This method is used to enable streaming of a HTTP request body* without internal buffering, when the content length is known in* advance.* <p>* An exception will be thrown if the application* attempts to write more data than the indicated* content-length, or if the application closes the OutputStream* before writing the indicated amount.* <p>* When output streaming is enabled, authentication* and redirection cannot be handled automatically.* A HttpRetryException will be thrown when reading* the response if authentication or redirection are required.* This exception can be queried for the details of the error.* <p>* This method must be called before the URLConnection is connected.* <p>* <B>NOTE:</B> {@link #setFixedLengthStreamingMode(long)} is recommended* instead of this method as it allows larger content lengths to be set.** @param   contentLength The number of bytes which will be written*          to the OutputStream.** @throws  IllegalStateException if URLConnection is already connected*          or if a different streaming mode is already enabled.** @throws  IllegalArgumentException if a content length less than*          zero is specified.** @see     #setChunkedStreamingMode(int)* @since 1.5*/public void setFixedLengthStreamingMode (int contentLength) {if (connected) {throw new IllegalStateException ("Already connected");}if (chunkLength != -1) {throw new IllegalStateException ("Chunked encoding streaming mode set");}if (contentLength < 0) {throw new IllegalArgumentException ("invalid content length");}fixedContentLength = contentLength;}

我是上传文件场景: 使用文件的大小作为长度

FileInputStream fileInputStream = new FileInputStream(uploadFileName);
long totalLength= fileInputStream.getChannel().size();
var boundary = "someboundary";
var temUploadUrl = "url path";
//
var url = new URL(temUploadUrl);
var connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("PUT");
connection.setRequestProperty("Content-Type", MediaType.MULTIPART_FORM_DATA_VALUE + "; boundary=" + boundary);connection.setDoOutput(true);
// 设置 Content-Length
connection.setRequestProperty("Content-Length", String.valueOf(totalLength)); 
connection.setFixedLengthStreamingMode(totalLength);

相关文章:

  • vr建筑虚拟实景展厅漫游体验更直观全面
  • running小程序重要技术流程文档
  • ubuntu-更改镜像源-系统初始化-安装Clion-C++编译环境-Java安装
  • Nginx的请求速率限制模块的两个关键参数rate和burst和相关代码语句的详细说明。
  • 使用阿里巴巴同步工具DataX实现Mysql与ElasticSearch数据同步
  • Python学习笔记-类
  • neuq-acm预备队训练week 8 P1144 最短路计数
  • VC++使用GetProcessTimes获取进程创建时间、销毁时间、用户态时间、内核态时间
  • 20231207给NanoPC-T4(RK3399)开发板刷Android12的挖掘机方案的LOG
  • Global IIIumination(GI)全局光照原理(一)3D空间全局光照
  • 【计算机网络实验】实验三 IP网络规划与路由设计(头歌)
  • 三. LiDAR和Camera融合的BEV感知算法-BEVFusion实战
  • 聚类算法的性能度量
  • MFC CLXHHandleEngine动态库-自定义设置对话框使用
  • 【线性代数与矩阵论】Jordan型矩阵
  • Angular 响应式表单之下拉框
  • CAP理论的例子讲解
  • electron原来这么简单----打包你的react、VUE桌面应用程序
  • Laravel Telescope:优雅的应用调试工具
  • PAT A1017 优先队列
  • Quartz实现数据同步 | 从0开始构建SpringCloud微服务(3)
  • vue-router的history模式发布配置
  • 汉诺塔算法
  • 理清楚Vue的结构
  • 普通函数和构造函数的区别
  • 时间复杂度与空间复杂度分析
  • 世界上最简单的无等待算法(getAndIncrement)
  • 事件委托的小应用
  • 收藏好这篇,别再只说“数据劫持”了
  • 我的业余项目总结
  • 新书推荐|Windows黑客编程技术详解
  • # Java NIO(一)FileChannel
  • #NOIP 2014# day.1 生活大爆炸版 石头剪刀布
  • #绘制圆心_R语言——绘制一个诚意满满的圆 祝你2021圆圆满满
  • (2)(2.10) LTM telemetry
  • (floyd+补集) poj 3275
  • (Java数据结构)ArrayList
  • (MTK)java文件添加简单接口并配置相应的SELinux avc 权限笔记2
  • (react踩过的坑)antd 如何同时获取一个select 的value和 label值
  • (STM32笔记)九、RCC时钟树与时钟 第一部分
  • (二)springcloud实战之config配置中心
  • (二)原生js案例之数码时钟计时
  • (十二)devops持续集成开发——jenkins的全局工具配置之sonar qube环境安装及配置
  • (十三)Flask之特殊装饰器详解
  • (十一)JAVA springboot ssm b2b2c多用户商城系统源码:服务网关Zuul高级篇
  • (原创)boost.property_tree解析xml的帮助类以及中文解析问题的解决
  • (转载)从 Java 代码到 Java 堆
  • .NET Core WebAPI中使用Log4net 日志级别分类并记录到数据库
  • .NET Core 中的路径问题
  • .Net Framework 4.x 程序到底运行在哪个 CLR 版本之上
  • .Net 访问电子邮箱-LumiSoft.Net,好用
  • .NET 设计模式初探
  • .NET 直连SAP HANA数据库
  • @NotNull、@NotEmpty 和 @NotBlank 区别
  • @Service注解让spring找到你的Service bean