Java文件下载ZIP报错:Out of Memory的问题排查
作者:山林竹笋
废话少说,上干货!!
项目中下载小文件或者下载成ZIP文件都正常,但下载超过2G的ZIP文件,报错:Out of Memory:Java Heap Space。
内存溢出,可是大毛病,排查一下代码:
private void setByteArrayOutputStream(String fileName, InputStream inputStream, ZipArchiveOutputStream zous) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = inputStream.read(buffer)) != -1) { baos.write(buffer, 0, len); } baos.flush(); byte[] bytes = baos.toByteArray(); //设置文件名 ArchiveEntry entry = new ZipArchiveEntry(fileName); zous.putArchiveEntry(entry); zous.write(bytes); zous.closeArchiveEntry(); baos.close(); }
细心的童鞋应该已经发现错误了!
一、原因分析
原因分析主要与 ByteArrayOutputStream
的使用有关。ByteArrayOutputStream 会在内存中动态扩展其缓冲区以容纳写入的数据。当写入大量数据时,尤其是在处理大文件时,可能会导致内存频繁分配和复制,从而消耗大量内存。大文件处理:如果输入流(InputStream)读取的数据量很大(如超过几百MB或GB),ByteArrayOutputStream 可能会消耗超过可用内存的资源,导致 OutOfMemoryError。
在上述代码中,逻辑如下:
(1)读取数据:通过 inputStream.read(buffer) 持续将数据读取到缓冲区 buffer 中。
(2)写入 ByteArrayOutputStream:每次读取的数据都被写入到 ByteArrayOutputStream 中。如果文件非常大,ByteArrayOutputStream 将不断扩展其内部数组。
(3) 转换为字节数组:调用 baos.toByteArray() 时,会创建一个新的字节数组并将所有数据复制到这个新数组中,这又需要额外的内存。
二、解决方案
采用流式处理:直接从 InputStream
读取并写入到 ZipArchiveOutputStream
,而不使用 ByteArrayOutputStream
。这样可以避免将整个文件加载到内存中。
代码如下:
private void setByteArrayOutputStream(String fileName, InputStream inputStream, ZipArchiveOutputStream zous) { //创建Zip入口 try { ZipArchiveEntry entry = new ZipArchiveEntry(fileName); zous.putArchiveEntry(entry); //使用流缓冲区,一次1024字节,分块读取并写入ZIP输出流 byte[] buffer = new byte[1024]; int len; while ((len = inputStream.read(buffer)) != -1) { zous.write(buffer, 0, len); } //完成当前ZIP入口 zous.closeArchiveEntry(); } catch (Exception ex) { ex.printStackTrace(); } finally { //确保输入流被关闭 try { inputStream.close(); } catch (Exception e) { e.printStackTrace(); } } }
运行之后,我们发现:不论多大的文件下载,不再报错内存溢出了。但对于超过了4G的文件,在形成ZIP包时,出现了其他错误,如下:
org.apache.commons.compress.archivers.zip.Zip64RequiredException: XXXXXXXXXXXXXXXXXXXXXXXXXXXX's size exceeds the limit of 4GByte. at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.checkIfNeedsZip64(ZipArchiveOutputStream.java:651) at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.handleSizesAndCrc(ZipArchiveOutputStream.java:638) at org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream.closeArchiveEntry(ZipArchiveOutputStream.java:513)
原因分析:
这个异常通常是在尝试创建一个 ZIP 文件时遇到的,特别是当文件的大小超过 4GB 时。ZIP 格式的标准限制了单个文件的大小为 4GB,因此在处理大文件时需要使用 ZIP64 格式。我们在代码里添加如下:
zous.setUseZip64(ZipArchiveOutputStream.Zip64Mode.Always); // 启用 ZIP64 支持 或者(不同版本,函数不一样) zous.setUseZip64(Zip64Mode.Always);
按照以上改完之后,我们发现文件下载ZIP完全没有内存溢出错误了。
到此这篇关于Java文件下载ZIP报错:Out of Memory的问题排查的文章就介绍到这了,更多相关Java文件下载ZIP报错内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
您可能感兴趣的文章:
- IDEA报错java.lang.OutOfMemoryError:Java heap space的解决办法
- IDEA报java: java.lang.OutOfMemoryError: Java heap space错误解决办法
- 解决idea出现的java.lang.OutOfMemoryError: Java heap space的问题
- 浅谈Java中OutOfMemoryError问题产生原因
- Java实战之OutOfMemoryError异常问题及解决方法
- java.lang.OutOfMemoryError: Metaspace异常解决的方法
- 实例解决Java异常之OutOfMemoryError的问题
- Java中内存异常StackOverflowError与OutOfMemoryError详解
- 完美解决java.lang.OutOfMemoryError处理错误的问题
- java.lang.OutOfMemoryError 错误整理及解决办法
- 解决Java中OutOfMemoryError的问题