Java文件下載ZIP報錯:Out of Memory的問題排查
廢話少說,上干貨??!
項目中下載小文件或者下載成ZIP文件都正常,但下載超過2G的ZIP文件,報錯:Out of Memory:Java Heap Space。
內(nèi)存溢出,可是大毛病,排查一下代碼:
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(); //設(shè)置文件名 ArchiveEntry entry = new ZipArchiveEntry(fileName); zous.putArchiveEntry(entry); zous.write(bytes); zous.closeArchiveEntry(); baos.close(); }
細心的童鞋應(yīng)該已經(jīng)發(fā)現(xiàn)錯誤了!
一、原因分析
原因分析主要與 ByteArrayOutputStream
的使用有關(guān)。ByteArrayOutputStream 會在內(nèi)存中動態(tài)擴展其緩沖區(qū)以容納寫入的數(shù)據(jù)。當(dāng)寫入大量數(shù)據(jù)時,尤其是在處理大文件時,可能會導(dǎo)致內(nèi)存頻繁分配和復(fù)制,從而消耗大量內(nèi)存。大文件處理:如果輸入流(InputStream)讀取的數(shù)據(jù)量很大(如超過幾百MB或GB),ByteArrayOutputStream 可能會消耗超過可用內(nèi)存的資源,導(dǎo)致 OutOfMemoryError。
在上述代碼中,邏輯如下:
(1)讀取數(shù)據(jù):通過 inputStream.read(buffer) 持續(xù)將數(shù)據(jù)讀取到緩沖區(qū) buffer 中。
(2)寫入 ByteArrayOutputStream:每次讀取的數(shù)據(jù)都被寫入到 ByteArrayOutputStream 中。如果文件非常大,ByteArrayOutputStream 將不斷擴展其內(nèi)部數(shù)組。
(3) 轉(zhuǎn)換為字節(jié)數(shù)組:調(diào)用 baos.toByteArray() 時,會創(chuàng)建一個新的字節(jié)數(shù)組并將所有數(shù)據(jù)復(fù)制到這個新數(shù)組中,這又需要額外的內(nèi)存。
二、解決方案
采用流式處理:直接從 InputStream
讀取并寫入到 ZipArchiveOutputStream
,而不使用 ByteArrayOutputStream
。這樣可以避免將整個文件加載到內(nèi)存中。
代碼如下:
?private void setByteArrayOutputStream(String fileName, InputStream inputStream, ZipArchiveOutputStream zous) { //創(chuàng)建Zip入口 try { ZipArchiveEntry entry = new ZipArchiveEntry(fileName); zous.putArchiveEntry(entry); //使用流緩沖區(qū),一次1024字節(jié),分塊讀取并寫入ZIP輸出流 byte[] buffer = new byte[1024]; int len; while ((len = inputStream.read(buffer)) != -1) { zous.write(buffer, 0, len); } //完成當(dāng)前ZIP入口 zous.closeArchiveEntry(); } catch (Exception ex) { ex.printStackTrace(); } finally { //確保輸入流被關(guān)閉 try { inputStream.close(); } catch (Exception e) { e.printStackTrace(); } } }
運行之后,我們發(fā)現(xiàn):不論多大的文件下載,不再報錯內(nèi)存溢出了。但對于超過了4G的文件,在形成ZIP包時,出現(xiàn)了其他錯誤,如下:
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)
原因分析:
這個異常通常是在嘗試創(chuàng)建一個 ZIP 文件時遇到的,特別是當(dāng)文件的大小超過 4GB 時。ZIP 格式的標(biāo)準(zhǔn)限制了單個文件的大小為 4GB,因此在處理大文件時需要使用 ZIP64 格式。我們在代碼里添加如下:
zous.setUseZip64(ZipArchiveOutputStream.Zip64Mode.Always); // 啟用 ZIP64 支持 或者(不同版本,函數(shù)不一樣) zous.setUseZip64(Zip64Mode.Always);
按照以上改完之后,我們發(fā)現(xiàn)文件下載ZIP完全沒有內(nèi)存溢出錯誤了。
到此這篇關(guān)于Java文件下載ZIP報錯:Out of Memory的問題排查的文章就介紹到這了,更多相關(guān)Java文件下載ZIP報錯內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- IDEA報錯java.lang.OutOfMemoryError:Java?heap?space的解決辦法
- IDEA報java:?java.lang.OutOfMemoryError:?Java?heap?space錯誤解決辦法
- 解決idea出現(xiàn)的java.lang.OutOfMemoryError:?Java?heap?space的問題
- 淺談Java中OutOfMemoryError問題產(chǎn)生原因
- Java實戰(zhàn)之OutOfMemoryError異常問題及解決方法
- java.lang.OutOfMemoryError: Metaspace異常解決的方法
- 實例解決Java異常之OutOfMemoryError的問題
- Java中內(nèi)存異常StackOverflowError與OutOfMemoryError詳解
- 完美解決java.lang.OutOfMemoryError處理錯誤的問題
- java.lang.OutOfMemoryError 錯誤整理及解決辦法
- 解決Java中OutOfMemoryError的問題
相關(guān)文章
Java中break、continue、return在for循環(huán)中的使用
這篇文章主要介紹了break、continue、return在for循環(huán)中的使用,本文是小編收藏整理的,非常具有參考借鑒價值,需要的朋友可以參考下2017-11-11Java?map和bean互轉(zhuǎn)常用的方法總結(jié)
這篇文章主要給大家介紹了關(guān)于Java中map和bean互轉(zhuǎn)常用方法的相關(guān)資料,平時日常Java開發(fā),經(jīng)常會涉及到Java?Bean和Map之間的類型轉(zhuǎn)換,需要的朋友可以參考下2023-09-09解決SpringBoot中LocalDateTime返回前端數(shù)據(jù)為數(shù)組結(jié)構(gòu)的問題
本文主要介紹了解決SpringBoot中LocalDateTime返回前端數(shù)據(jù)為數(shù)組結(jié)構(gòu)的問題,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-03-03java.Net.UnknownHostException異常處理問題解決
這篇文章主要介紹了java.Net.UnknownHostException異常處理方法,問題原因是在系統(tǒng)的?/etc/Hostname中配置了主機名,而在/etc/hosts文件中沒有相應(yīng)的配置,本文給大家詳細講解,需要的朋友可以參考下2023-03-03Spring Boot高效數(shù)據(jù)聚合之道深入講解
這篇文章主要給大家介紹了關(guān)于Spring Boot高效數(shù)據(jù)聚合之道的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06SpringBoot實現(xiàn)對Http接口進行監(jiān)控的代碼
Spring Boot Actuator是Spring Boot提供的一個模塊,用于監(jiān)控和管理Spring Boot應(yīng)用程序的運行時信息,本文將介紹一下Spring Boot Actuator以及代碼示例,以及如何進行接口請求監(jiān)控,需要的朋友可以參考下2024-07-07Spring+MyBatis實現(xiàn)數(shù)據(jù)庫讀寫分離方案
本文主要介紹了Spring+MyBatis實現(xiàn)數(shù)據(jù)庫讀寫分離方案。具有一定的參考價值,下面跟著小編一起來看下吧2017-01-01