2017-02-15 88 views
1

我使用java.util.zip.Deflater來壓縮通過HTTP PUT接收的數據流,這意味着我得到的數據用點滴和單調壓縮,不是一次全部壓縮。 調用java.util.zip.Deflater.setInput多次在第一次之後什麼也不做

byte[] compress(byte[] input) { 
    byte[] output = null; 
    if (this.compressor == null) { 
     this.compressor = new Deflater(Deflater.BEST_COMPRESSION, true); 

     // Add gzip header: 
     output = getGzipHeader(); 
    } 

    this.compressor.setInput(input); 
    this.compressor.finish(); 

    while (!this.compressor.finished()) { 
     byte[] tempOutput = new byte[10240]; 

     int compressedLength = this.compressor.deflate(tempOutput); 

     if (output == null) { 
      output = Arrays.copyOf(tempOutput, compressedLength); 
     } else { 
      byte[] newOutput = Arrays.copyOf(output, output.length + compressedLength); 
      System.arraycopy(tempOutput, 0, newOutput, output.length, compressedLength); 
      output = newOutput; 
     } 
    } 

    // Update CRC: 
    this.crc.update(input); 
    this.byteCount += input.length; 

    return output 
} 

當然包含此方法的類有實例變量:

private Deflater compressor; 
private CRC32 crc = new CRC32(); 
private long byteCount = 0; 

而且一旦這樣的數據來自於我的HTTP請求處理程序反覆調用,看起來像這樣的方法從HTTP請求收到最後一個字節,我附加CRC和來自crcbyteCount實例變量的總未壓縮長度。

只要我在HTTP PUT中發送非常少量的數據,這個工作很好,因爲compress方法只被調用一次。我結束了一個有效的gzip文件。只要我發送超過幾百字節,導致compress不止一次被調用,它不起作用,因爲在第一次調用後所有後續調用compressthis.compressor.finished()返回true,即使我調用this.compressor.setInput(input)與新的輸入數據。如果在處理完所有數據後查看this.compressor.getBytesRead(),則該調用返回的值恰好是第一個輸入緩衝區的大小(第一個調用this.compressor.setInput(input))。對該方法的後續調用都不會增加由getBytesRead()返回的值。

如果我在撥打setInput()後沒有撥打finish(),它根本不起作用 - 我沒有輸出。但好像打電話finish()是告訴Deflater不接受任何多輸入。

我在做什麼錯?

+1

做這個幫助:[DeflaterOutputStream](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/zip/DeflaterOutputStream.java#119 )? (並且直接使用這個類會有意義嗎?) – jtahlborn

+0

正如jtahlborn所說,DeflaterOutputStream會大大地幫助。作爲一般性評論,如果您使用'ArrayList.addAll(Arrays.asList(tempOutput))'而不是自己完成所有數組操作,那麼您的代碼看起來會更簡單。 –

+0

@jtahlborn,不,據我所知,沒有辦法讓我使用DeflatorOutputStream,因爲我沒有輸出流寫入。我可能應該提到我的服務是使用[Vert.x](http://vertx.io)構建的,所以我正在從[ReadStream](http://vertx.io/docs/apidocs/index .html?io/vertx/core/streams/ReadStream.html)並寫入[AsyncFile](http://vertx.io/docs/apidocs/index.html?io/vertx/core/file/AsyncFile.html )。數據從字節緩衝區中的ReadStream接收並寫入字節緩衝區中的AsyncFile。沒有輸入或輸出流。 –

回答

0

問題已解決。我基本上從DeflaterOutputStream.write(...)複製代碼幾乎verbatiim到我的compress方法,並從DeflaterOutputStream.close()複製到我自己的finish方法,它的效果很好。

這裏的竅門中的Javadoc Deflater未解釋甚至一點點(實際上違背了在javadoc中顯示的示例代碼)是在接收輸入,並調用deflater.setInput(...),你只檢查!deflater.needsInput(),並不適用於!deflater.finished()。然後,只有收到所有輸入後,纔會調用deflater.finish(),然後至關重要的是,您將通過循環while (!deflater.finished())來處理Deflater緩衝區中可能未決的所有剩餘數據,並繼續泄放任何剩餘數據。

有關詳細信息,請打開DeflaterOutputStream的來源並查看其write(byte[] b, int off, int len)finish()方法。

相關問題