Java文件下載ZIP報錯:Out of Memory的問題排查
廢話少說,上干貨??!
項(xiàng)目中下載小文件或者下載成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();
}細(xì)心的童鞋應(yīng)該已經(jīng)發(fā)現(xiàn)錯誤了!
一、原因分析
原因分析主要與 ByteArrayOutputStream 的使用有關(guān)。ByteArrayOutputStream 會在內(nèi)存中動態(tài)擴(kuò)展其緩沖區(qū)以容納寫入的數(shù)據(jù)。當(dāng)寫入大量數(shù)據(jù)時,尤其是在處理大文件時,可能會導(dǎo)致內(nèi)存頻繁分配和復(fù)制,從而消耗大量內(nèi)存。大文件處理:如果輸入流(InputStream)讀取的數(shù)據(jù)量很大(如超過幾百M(fèi)B或GB),ByteArrayOutputStream 可能會消耗超過可用內(nèi)存的資源,導(dǎo)致 OutOfMemoryError。
在上述代碼中,邏輯如下:
(1)讀取數(shù)據(jù):通過 inputStream.read(buffer) 持續(xù)將數(shù)據(jù)讀取到緩沖區(qū) buffer 中。
(2)寫入 ByteArrayOutputStream:每次讀取的數(shù)據(jù)都被寫入到 ByteArrayOutputStream 中。如果文件非常大,ByteArrayOutputStream 將不斷擴(kuò)展其內(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();
}
}
}運(yùn)行之后,我們發(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實(shí)戰(zhàn)之OutOfMemoryError異常問題及解決方法
- java.lang.OutOfMemoryError: Metaspace異常解決的方法
- 實(shí)例解決Java異常之OutOfMemoryError的問題
- Java中內(nèi)存異常StackOverflowError與OutOfMemoryError詳解
- 完美解決java.lang.OutOfMemoryError處理錯誤的問題
- java.lang.OutOfMemoryError 錯誤整理及解決辦法
- 解決Java中OutOfMemoryError的問題
相關(guān)文章
關(guān)于IntelliJ IDEA 打包代碼報錯的問題
這篇文章主要介紹了關(guān)于IntelliJ IDEA 打包代碼報錯的問題,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03
spring源碼閱讀--aop實(shí)現(xiàn)原理講解
這篇文章主要介紹了spring源碼閱讀--aop實(shí)現(xiàn)原理講解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-09-09
使用kafka-console-consumer.sh不停報WARN的問題及解決
這篇文章主要介紹了使用kafka-console-consumer.sh不停報WARN的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-03-03
Java實(shí)現(xiàn)十秒向MySQL插入百萬條數(shù)據(jù)
這篇文章主要為大家詳細(xì)介紹了Java如何實(shí)現(xiàn)十秒向MySQL插入百萬條數(shù)據(jù),文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)或工作有一定借鑒價值,需要的可以參考一下2022-11-11
Java二分查找算法與數(shù)組處理的應(yīng)用實(shí)例
二分查找法,又叫做折半查找法,它是一種效率較高的查找方法。數(shù)組對于每一門編程語言來說都是重要的數(shù)據(jù)結(jié)構(gòu)之一,當(dāng)然不同語言對數(shù)組的實(shí)現(xiàn)及處理也不盡相同。Java 語言中提供的數(shù)組是用來存儲固定大小的同類型元素2022-07-07
SpringBoot+EasyExcel實(shí)現(xiàn)自定義復(fù)雜樣式導(dǎo)入導(dǎo)出
這篇文章主要為大家詳細(xì)介紹了SpringBoot如何結(jié)果EasyExcel實(shí)現(xiàn)自定義復(fù)雜樣式導(dǎo)入導(dǎo)出功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下2025-07-07
Java實(shí)現(xiàn)百萬數(shù)據(jù)分批次插入的最佳實(shí)踐分享
在當(dāng)今數(shù)據(jù)驅(qū)動的應(yīng)用開發(fā)中,處理大批量數(shù)據(jù)插入是后端開發(fā)人員經(jīng)常面臨的挑戰(zhàn),隨著業(yè)務(wù)規(guī)模的擴(kuò)大,數(shù)據(jù)量呈現(xiàn)爆發(fā)式增長,傳統(tǒng)單條插入方式在面對百萬級數(shù)據(jù)時顯得力不從心,所以本文給大家分享了Java實(shí)現(xiàn)百萬數(shù)據(jù)分批次插入的最佳實(shí)踐,需要的朋友可以參考下2025-07-07

