多次调用java.util.zip.Deflater.setInput后第一次没有任何操作

时间:2017-02-15 17:34:25

标签: java http gzip

我使用java.util.zip.Deflater来压缩通过HTTP PUT接收的传入数据流,这意味着我将数据压缩为单调,而不是一次性压缩。所以我的HTTP请求处理程序在数据进入时反复调用一个看起来像这样的方法:

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请求收到最后一个字节,我就会在crcbyteCount个实例变量中附加CRC和未压缩的总长度。

只要我在HTTP PUT中发送非常少量的数据,这就行得很好,因为compress方法只被调用一次。我最终得到了一个有效的gzip文件。一旦我发送超过几百个字节,导致compress被多次调用,它就不起作用,因为在第一个compress之后的this.compressor.finished()之后的所有后续调用中this.compressor.setInput(input) }返回true,即使我已使用新输入数据调用this.compressor.getBytesRead()。如果在处理完所有数据后查看this.compressor.setInput(input),则该调用返回的值正好是第一个输入缓冲区的大小(第一次调用getBytesRead())。对该方法的后续调用都不会增加finish()返回的值。

如果我在致电setInput()后没有给finish()打电话,那么它根本不起作用 - 我没有输出。但似乎调用Deflater告诉IRBuilder不要接受任何更多输入。

我做错了什么?

1 个答案:

答案 0 :(得分:0)

问题解决了。我基本上将代码几乎从DeflaterOutputStream.write(...)复制到我的compress方法中,并从DeflaterOutputStream.close()复制到我自己的finish方法中,效果很好。

这里的诀窍甚至没有解释Deflater的javadoc中的一小部分(实际上与该javadoc中显示的示例代码相矛盾)是在接收输入和调用deflater.setInput(...)时,您只检查!deflater.needsInput(),而不检查!deflater.finished()。然后,只有在收到所有输入后,才会调用deflater.finish(),然后至关重要的是,然后循环Deflater并继续处理while (!deflater.finished())缓冲区中可能待处理的所有剩余数据缩小任何剩余数据。

有关详细信息,只需打开DeflaterOutputStream的来源,然后查看其write(byte[] b, int off, int len)finish()方法。