我使用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请求收到最后一个字节,我就会在crc
和byteCount
个实例变量中附加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
不要接受任何更多输入。
我做错了什么?
答案 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()
方法。