客戶說,上傳檔案的時候出了問題。說無法上傳某些檔案,說是抛java.io.IOException: Corrupt GZIP trailer異常,但并不是所有檔案都會抛異常。僅限于某幾個檔案(腦爛了~),客戶還把“有問題”的檔案發了過來。一個叫testFile.bbc的檔案,大小是97,663位元組,還有一個叫 測試檔案.bbd,大小是2,325,310位元組。
BUG從天而降
上傳檔案這部分的測試是在Windows環境下做的。當時測試的時候也沒遇到什麼問題。可是客戶說他們是在Unix系統下測試時出的問題,Windows系統一切正常(腦爛了~)。為了重制這個BUG,我們裝了Solaris系統。并且安裝了Eclipse和Weblogic的Solaris版本,将服務端工程載入Eclipse并啟動。啟動用戶端的浏覽器(Windows,IE6),上傳檔案,結果上傳失敗服務端抛出java.io.IOException: Corrupt GZIP trailer。這到底是為什麼?
問題的描述
<script type="text/javascript"> google_ad_client="pub-6065469188450680"; google_ad_width=728; google_ad_height=90; google_ad_format="728x90_as"; google_ad_type="text_image"; google_ad_channel="6872543818"; </script>1.系統構成描述:
用戶端是一個APPLET,該APPLET的功能是将使用者指定的檔案以.gz的形式壓縮以後發送到服務端。
服務端是一個SERVLET,它的作用是将用戶端發送過來的.gz檔案解壓出來,并存在伺服器的某個目錄中。
2.發送資料中的發送檔案格式描述:
第一步:發送 --Boundary(這裡的Boundary是一個時間标簽,用來分割資料)回車換行
第二步:發送 Content-Disposition: form-data; name=uploadfile;filename=testFile.bbc回車換行
第三步:發送 Content-type:x-gzip回車換行
第四步:發送 回車換行
第五步:發送 .gz檔案内容
第六步:發送 回車換行
第七步:發送 --Boundary--
3.用戶端按照描述2所述的格式将資料發送到服務端。服務端按照該格式來解析資料,将資料中的檔案内容部分抽出來并寫入.gz檔案中。
投機取巧
考慮到每次抛異常都是在讀最後一次資料的時候,是以我們将原來每次讀1024個位元組,改為每次讀1個Int,并且如果在讀最後一個Int的時候抛出Corrupt GZIP trailer異常,我們就不理。沒有了異常,表面上來看問題似乎已經解決了
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer protected void bufferedCopy( final BufferedInputStream aoInput, final BufferedOutputStream aoOutput)
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer throws IOException ... {
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer // int niRead;
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer // byte[] noData;
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer //
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer // noData = new byte[getBufferSize()];
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer //
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer // while ((niRead = aoInput.read(noData)) > 0) {
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer // aoOutput.write(noData, 0, niRead);
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer // }
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer int n = aoInput.read();
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer try ...{
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer while (n != -1) ...{
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer aoOutput.write(n);
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer n = aoInput.read();
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer }
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer } catch (IOException e) ...{
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer if (e.getMessage ().indexOf ("Corrupt GZIP trailer") == -1) ...{
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer throw e;
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer }
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer }
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer aoOutput.flush();
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer }
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer 紙永遠包不住火
我們一邊慶祝BUG的成功修改,一邊進入測試階段。我随便上傳了幾個客戶傳來的“問題檔案”,結果都OK。沒有問題。但是當我把用戶端的檔案和伺服器上的檔案拿來用HexWorkShop一比較,有了一個驚人的發現。兩個檔案竟然不一樣。而且隻差一個位元組(腦爛了~!)。
問題分析:
為了找尋問題的根源,我們進行了跟蹤調試。結果發現:
1.在用戶端将檔案壓縮成.gz檔案之後,發送資料之前,利用winrar打開壓縮包解壓測試,沒有出問題。
2.服務端接收到.gz檔案之後,解壓檔案之前,将服務端的testFile.bbc.gz複制到Windows環境下,并用winrar打開,結果CRC校驗錯誤[c:/testFile.bbc.gz CRC failed in testFile.bbc. The file is corrupt]
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer That's mean 服務端收到檔案後,解壓縮之前,檔案已經是壞的了。難怪會抛Corrupt GZIP trailer異常。
追根溯源
經過反複的跟蹤調試終于找到了問題的出處。問題就出在服務端解析資料的過程中。
.gz檔案讀入(原來的寫法): <script src="http://pagead2.googlesyndication.com/pagead/show_ads.js" type="text/javascript"></script>
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer boolean nbCutSeparator = false ;
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer while ((niRead = aoInput.readLine(noData, 0 , m_BufferSize)) > - 1 ) ... {
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer nsLine = new String(noData, 0, niRead);
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer if (isBoundary(nsLine)) ...{
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer break;
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer } else if(nbCutSeparator) ...{
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer // 如果下一行不是Boundary,是以将CRLF寫入檔案
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer nbCutSeparator = false;
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer noBO.write("/r/n".getBytes());
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer }
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer // 除了回車換行符以外的資料寫入檔案
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer if(nsLine.endsWith("/r/n")) ...{
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer nbCutSeparator = true;
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer niRead = niRead - "/r/n".length();
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer }
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer noBO.write(noData, 0 , niRead);
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer }
.gz檔案讀入(修正後的寫法):
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer while ((niRead = aoInput.readLine(noData, 0 , m_BufferSize)) > - 1 ) ... {
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer nsLine = new String(noData, 0, niRead);
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer if (isBoundary(nsLine)) ...{
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer if (oldData != null && oldData.length > 0) ...{
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer if (oldData.length >= 2
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer && oldData[oldData.length - 2] == (byte) 0x13
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer && oldData[oldData.length - 1] == (byte) 0x10) ...{
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer if (oldData.length > 2) ...{
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer noBO.write(oldData, 0, oldData.length - 2);
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer }
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer } else ...{
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer noBO.write(oldData);
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer }
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer }
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer break;
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer }
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer if (oldData != null && oldData.length > 0) ...{
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer noBO.write(oldData);
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer }
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer oldData = new byte[niRead];
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer for (int i = 0; i < niRead; i++) ...{
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer oldData[i] = noData[i];
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer }
由于WINDOWS和UNIX中輸入換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer }
後記
至今我還不是很清楚,為什麼在Unix下,第一種寫法會有問題。并且也不清楚在什麼情況下會發生問題。
我隻知道一定是和回車換行符有關系的,我僅僅是換了一個方法來複制檔案,問題就莫名其妙的解決了。
可是,這到底是為什麼呢?
那好吧,就寫到這裡,哪位高人如能指點一二,在下感激不盡~
之前我在網上看到JDK有一個相關的BUG:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4262583
但是,現在看來,這個問題和SUN的BUG沒太大關系吧?應該是算法上的問題吧?
這位仁兄貌似也遇到了和我類似的問題,好像問題也得到了解決
http://forum.java.sun.com/thread.jspa?threadID=719980&messageID=4154773