如何通过Camel-Jetty组件流式上传大文件以避免OutOfMemoryError
问题
使用Camel-Jetty组件时,如何通过multipart/form-data格式流式上传大文件(无需将整个文件加载到内存即可立即处理)?当前上传大文件时抛出内存溢出错误:
java.lang.IllegalStateException: java.lang.OutOfMemoryError: Java heap space
当前使用的Camel路由代码如下:
public class InputHttpRoute extends RouteBuilder { @Override public void configure() throws Exception { from(http()) .noStreamCaching() .process(e -> { e.getIn(AttachmentMessage.class).getAttachments() .forEach((s, dataHandler) -> { InputStream files = null; try { files = dataHandler.getInputStream(); byte[] buffer = new byte[10500]; int b; while ((b = files.read(buffer)) != -1) { // Convert byte to character String data = new String(buffer, 0, b); System.err.println(data); } } catch (IOException ex) { throw new RuntimeException(ex); } }); }); // .setBody(constant("xxx")) ; } private static String http() { return new StringBuilder("jetty:http://0.0.0.0:8787/email/myservice") .append("?httpMethodRestrict=") .append("POST") .append("&disableStreamCache=true") .append("&eagerCheckContentAvailable=true") .append("&exchangePattern=InOnly") .append("&chunked=true") .append("&requestBufferSize=0") .append("&responseBufferSize=0") /* .append("&requestBufferSize=") .append(100) .append("&requestHeaderSize=") .append(100) .append("&disableStreamCache=true")*/ .toString(); } }
解决方案
1. 配置Jetty端点,强制流式处理Multipart
Jetty默认会将Multipart内容缓存到内存或临时文件,需通过参数配置强制流式处理:
- 移除
eagerCheckContentAvailable=true:该参数会让Jetty提前读取整个请求到内存,与流式处理冲突 - 添加
multipartConfig参数:设置不限制文件大小、跳过内存缓存阈值
修改后的http()方法:
private static String http() { return new StringBuilder("jetty:http://0.0.0.0:8787/email/myservice") .append("?httpMethodRestrict=POST") .append("&disableStreamCache=true") .append("&exchangePattern=InOnly") .append("&chunked=true") .append("&requestBufferSize=0") .append("&responseBufferSize=0") .append("&multipartConfig={{location=/, maxFileSize=-1, maxRequestSize=-1, fileSizeThreshold=0}}") .toString(); }
2. 优化文件处理逻辑,减少内存占用
- 使用
try-with-resources自动关闭输入流,避免资源泄漏 - 避免将二进制字节直接转成
String(非文本文件会乱码且增加内存开销),直接操作字节流即可 - 使用标准大小的缓冲区(如8KB),减少内存波动
修改后的Processor逻辑:
.process(e -> { AttachmentMessage attachmentMsg = e.getIn(AttachmentMessage.class); attachmentMsg.getAttachments().forEach((fileName, dataHandler) -> { try (InputStream inputStream = dataHandler.getInputStream()) { byte[] buffer = new byte[8192]; int readBytes; while ((readBytes = inputStream.read(buffer)) != -1) { // 此处编写流式处理逻辑,比如写入文件、转发到消息队列等 // 示例:Files.write(Paths.get("/tmp/" + fileName), buffer, 0, readBytes, StandardOpenOption.CREATE, StandardOpenOption.APPEND); } } catch (IOException ex) { throw new RuntimeException("处理文件[" + fileName + "]失败", ex); } }); })
3. 额外优化
- 全局禁用StreamCaching:在Camel上下文初始化时添加
camelContext.getStreamCachingStrategy().setEnabled(false);,确保没有隐式的流缓存 - 调整JVM堆内存参数(如
-Xmx512m)作为兜底,但这不是解决内存溢出的根本方案
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

