多次调用java.util.zip.Deflater.setInput在第一次调用后不会产生任何效果

多次调用java.util.zip.Deflater.setInput在第一次调用后不会产生任何效果,java,http,gzip,Java,Http,Gzip,我正在使用java.util.zip.Deflater来压缩通过HTTP PUT接收的传入数据流,这意味着我将以点滴方式压缩数据,而不是一次压缩所有数据。因此,当数据进入时,我的HTTP请求处理程序会重复调用如下所示的方法: 当然,包含此方法的类具有实例变量: 一旦从HTTP请求接收到最后的字节,我就从CRC和byteCount实例变量中附加CRC和总未压缩长度 只要我在HTTP PUT中发送非常少量的数据,这就非常有效,因为compress方法只被调用一次。我最终得到了一个有效的gzip文

我正在使用java.util.zip.Deflater来压缩通过HTTP PUT接收的传入数据流,这意味着我将以点滴方式压缩数据,而不是一次压缩所有数据。因此,当数据进入时,我的HTTP请求处理程序会重复调用如下所示的方法:

当然,包含此方法的类具有实例变量:

一旦从HTTP请求接收到最后的字节,我就从
CRC
byteCount
实例变量中附加CRC和总未压缩长度

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

如果在调用
setInput()
之后我没有调用
finish()
,那么它根本不起作用——我没有得到任何输出。但是调用
finish()
似乎是告诉
Deflater
不要再接受任何输入


我做错了什么?

问题解决了。我基本上是从
DeflaterOutputStream.write(…)
复制到我的
compress
方法中,然后从
DeflaterOutputStream.close()
复制到我自己的
finish
方法中,效果非常好

这里的诀窍在javadoc中甚至没有解释一点点关于
Deflater
(事实上与该javadoc中显示的示例代码相矛盾)是,在接收输入并调用
Deflater.setInput(…)
时,您只检查
!deflater.needsInput()
,而不是用于
!deflater.finished()
。然后,只有在接收到所有输入后,才调用
deflater.finish()
,关键是,通过在(!deflater.finished())期间循环
,并继续对任何剩余数据进行放气,从而处理
deflater
缓冲区中可能挂起的任何剩余数据


有关详细信息,只需打开
DeflaterOutputStream
的源代码,并查看其
写入(字节[]b,int off,int len)
finish()
方法。

问题已解决。我基本上是从
DeflaterOutputStream.write(…)
复制到我的
compress
方法中,然后从
DeflaterOutputStream.close()
复制到我自己的
finish
方法中,效果非常好

这里的诀窍在javadoc中甚至没有解释一点点关于
Deflater
(事实上与该javadoc中显示的示例代码相矛盾)是,在接收输入并调用
Deflater.setInput(…)
时,您只检查
!deflater.needsInput()
,而不是用于
!deflater.finished()
。然后,只有在接收到所有输入后,才调用
deflater.finish()
,关键是,通过在(!deflater.finished())
期间循环
,并继续对任何剩余数据进行放气,从而处理
deflater
缓冲区中可能挂起的任何剩余数据


有关详细信息,只需打开
DeflaterOutputStream
的源代码,并查看其
写入(字节[]b,int off,int len)
finish()
方法。

这有帮助吗:?(直接使用这个类有意义吗?)。一般来说,如果您使用
ArrayList.addAll(Arrays.asList(tempOutput))
而不是自己进行所有的数组操作,您的代码看起来会简单得多。@jtahlborn,不,据我所知,我无法使用DeflatorOutputStream,因为我没有要写入的输出流。我可能应该提到,我的服务是使用构建的,所以我正在从a阅读并向a写作。数据从字节缓冲区中的读取流接收,并写入字节缓冲区中的异步文件。没有输入或输出流。@KlitosKyriacou,没有,这将添加整个tempOutput缓冲区,包括所有0字节。我正在寻找关于手头问题的帮助,能够基于对输入数据流的读取以小块的形式编写压缩数据——我对一般的编码建议不感兴趣,谢谢。为了清楚地说明具体问题,本文对代码进行了简化。您可以实现一个OutputStream,将数据写入异步文件。这有帮助吗:?(直接使用这个类有意义吗?)。一般来说,如果您使用
ArrayList.addAll(Arrays.asList(tempOutput))
而不是自己进行所有的数组操作,您的代码看起来会简单得多。@jtahlborn,不,据我所知,我无法使用DeflatorOutputStream,因为我没有要写入的输出流。我可能应该提到,我的服务是使用构建的,所以我正在从a阅读并向a写作。数据从字节缓冲区中的读取流接收,并写入字节缓冲区中的异步文件。没有输入或输出流。@KlitosKyriacou,没有,这将添加整个tempOutput缓冲区,包括所有0字节。我正在寻求公关方面的帮助
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;