多次调用java.util.zip.Deflater.setInput在第一次调用后不会产生任何效果
我正在使用java.util.zip.Deflater来压缩通过HTTP PUT接收的传入数据流,这意味着我将以点滴方式压缩数据,而不是一次压缩所有数据。因此,当数据进入时,我的HTTP请求处理程序会重复调用如下所示的方法: 当然,包含此方法的类具有实例变量: 一旦从HTTP请求接收到最后的字节,我就从多次调用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文
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;