2017-02-15 33 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()方法。

相关问题