Java 7大文件转Blob遇堆内存不足,求流式处理方案
解决大文件MultipartFile转Blob的堆内存溢出问题(Java 7)
首先,你碰到的问题根源很明确:blob.setBytes(1, file.getBytes())会把4-5GB的文件一次性全加载到JVM堆内存里,直接触发OOM。流式处理的核心思路就是分块读写,绝不把整个文件塞进内存,利用Blob的二进制流接口就能实现这个目标。
下面是Java 7环境下的改造方案:
核心改造代码
def uploadFile(MultipartFile file) { Sql sql = utilsService.retrieveSQLFromDataSource() Connection conn = dataSource.getConnection(); Blob blob = conn.createBlob(); InputStream inputStream = null; OutputStream blobOutputStream = null; try { // 获取文件输入流和Blob的输出流 inputStream = file.getInputStream(); blobOutputStream = blob.setBinaryStream(1); // 用缓冲区分块传输,每次只加载8KB数据到内存 byte[] buffer = new byte[8192]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { blobOutputStream.write(buffer, 0, bytesRead); } // 这里执行你的SQL操作,比如插入Blob到数据库 // sql.executeUpdate("INSERT INTO file_storage (file_data) VALUES (?)", blob); } catch (IOException | SQLException e) { throw new RuntimeException("大文件上传失败", e); } finally { // 手动关闭所有资源,避免泄漏 if (blobOutputStream != null) { try { blobOutputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } if (conn != null) { try { conn.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
关键细节说明
- 流式读写逻辑:通过
file.getInputStream()拿到文件的流式输入,blob.setBinaryStream(1)获取Blob的流式输出,用固定大小的缓冲区循环读写,全程只有8KB数据在内存里流转,彻底解决堆内存溢出问题。 - 简化资源管理:Java 7已经支持
try-with-resources语法,能自动关闭实现了Closeable接口的资源,你也可以用这个写法简化代码:try (Connection conn = dataSource.getConnection(); InputStream inputStream = file.getInputStream(); OutputStream blobOutputStream = conn.createBlob().setBinaryStream(1)) { // 缓冲区拷贝逻辑 byte[] buffer = new byte[8192]; int bytesRead; while ((bytesRead = inputStream.read(buffer)) != -1) { blobOutputStream.write(buffer, 0, bytesRead); } // 执行SQL操作 } catch (Exception e) { throw new RuntimeException("大文件上传失败", e); } - 数据库驱动适配:部分数据库驱动需要额外配置才能支持流式Blob写入,比如MySQL JDBC驱动要在URL里加上
useServerPrepStmts=true&rewriteBatchedStatements=true,确保大字段能流式写入数据库。 - 缓冲区大小调整:8KB是比较通用的缓冲区大小,你可以根据服务器内存和磁盘IO性能,调整到4KB-16KB之间。
额外注意事项
- 要给服务器配置足够长的连接超时时间,大文件上传可能需要几分钟甚至更久,避免中途断开连接。
- 如果用Spring框架管理事务,记得调整
@Transactional的超时时间,默认超时可能不够用。 - 全程避免调用
file.getBytes()、file.transferTo(new File(...))这类会一次性加载整个文件的方法,所有操作都用流来完成。
内容的提问来源于stack exchange,提问作者Pawel
相关产品推荐
相关产品推荐

