天天看點

由于WINDOWS和UNIX中回車換行符的表示方法不同而引起的.gz檔案的損壞Corrupt GZIP trailer

  客戶說,上傳檔案的時候出了問題。說無法上傳某些檔案,說是抛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