近來對院社二維碼平臺進行2.0升級改造。於昨日踩到一個巨坑。特此記錄。。。java
需求源於院社編輯在批量下載二維碼的時候,系統後臺須要對所要下載的二維碼進行重命名和zip打包壓縮。linux
系統測試的時候發現:首次請求批量下載時,也即壓縮文件還未生成時,後臺能夠正常壓縮文件並提供下載。可是第二次請求批量下載時,網頁一直無反應。。。windows
嘗試了幾回後仍舊沒反應。只好查看tomcat日誌,驚奇的發現日誌只寫了一半,後半部分丟失(第一次遇到這種狀況)==|||tomcat
不過老天爺保佑,寫入的一部分顯示:No space left device.測試
我擦!硬盤滿了?昨天還有68%的餘量。今天就沒了?google
迅速df du命令走起。du顯示並無佔滿。可是df顯示已經100%。這是搞毛。。spa
google一下,發現du df顯示結果不同的緣由多是有文件句柄沒有釋放,文件仍舊被進程佔用。df統計的是硬盤實際佔用,而du並不包括已經標記刪除卻仍舊被進程佔用,實際上並未物理刪除的文件。(文件物理刪除和標識爲deleted不是一個概念)操作系統
接着調用lsof | grep deleted查看文件佔用狀況。。果真那幾個zip文件size已經突破天際了。。指針
看來是java對zip文件打包時出錯了。陷入了死循環???日誌
因爲zip打包源碼是同事提供的,並無深刻了解。不得不扒開package,查看究竟是個啥子邏輯。
通過一番折騰。終於發現問題。
舉個例子:
a文件下有1.jpg 2.jpg兩個文件
在第一次請求批量下載時,生成了b.zip文件。
如此a文件夾下就有了1.jpg 2.jpg b.zip文件了
根據源碼邏輯,首先會對a文件夾進行遍歷搜索,而後將每一個文件逐個加入zip文件中。
那麼,第二次請求時,從表面上看,可能會粗略的覺得b.zip會被覆蓋掉,替換成新b.zip,裏面包括1.jpg 2.jpg 和舊的b.zip。
大錯特錯!
文件在進行寫操做時,始終是對同一個b.zip在操做!
分解一下過程。首先在遍歷a文件夾獲得三個文件名的列表:1 2 b
建立新b時,舊b文件會被刪除,可是b這個文件名仍舊保留在上面的文件列表中。
接下來,添加1到新b,添加2到新b。
在添加舊B的時候,實則在對新B操做!!若是從文件讀寫指針的角度來看,以下圖所示
read write
1 2 (12)
能夠看到,因爲是在對同一個文件操做,read指針永遠不可能遇上write,也即EOF,那麼這個寫就永無止境。
因此解決bug的方法是:把要打包的文件和目標zip文件放在兩個不一樣的文件夾下面。這樣就不會始終對同一個zip文件又讀又寫了。
此外,我還在windows平臺上進行了測試。發現一個很詭異的現象。在windows上文件可以正常下載,且不會出現磁盤容量爆棚的狀況。可是:
1)新打包的b.zip裏面還有一個b.zip。
2)外層的b.zip能夠正常解壓,裏層的b.zip也能夠解壓和查看。可是裏層的b.zip解壓時會報文件末端錯誤。
3)裏層的b.zip還有一個b.zip,暫且叫作裏裏層b.zip。這個裏裏層b.zip就徹底打不開了。
4)用資源管理器查看tomcat所掌握的資源句柄,仍舊包括這個最外層的b.zip。也就是即使client完成了下載,服務端也沒有釋放這個資源。
能夠說,windows遇到了和linux平臺同樣的問題。可是可能由於windows操做系統的文件系統具體細節的處理方式不一樣,致使出現了最後這樣一個相對詭異的結果。