问题
I want to compress some data, so I came across the DeflatorInputStream & DeflatorOutputStream classes. However, the following example shows that I can't seem to reconstruct my original data when using these classes.
When I switch to a ZipInputStream and ZipOutputStream it does work, but since I don't need zip files per se, I thought a generic compression would be better. Mainly I'm interested in understanding why this example doesn't work.
//Create some "random" data
int bytesLength = 1024;
byte[] bytes = new byte[bytesLength];
for(int i = 0; i < bytesLength; i++) {
bytes[i] = (byte) (i % 10);
}
//Compress the data, and write it to somewhere (a byte array for this example)
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
DeflaterOutputStream outputStream = new DeflaterOutputStream(arrayOutputStream);
outputStream.write(bytes);
//Read and decompress the data
byte[] readBuffer = new byte[5000];
ByteArrayInputStream arrayInputStream = new ByteArrayInputStream(arrayOutputStream.toByteArray());
DeflaterInputStream inputStream = new DeflaterInputStream(arrayInputStream);
int read = inputStream.read(readBuffer);
//Should hold the original (reconstructed) data
byte[] actuallyRead = Arrays.copyOf(readBuffer, read);
//Results differ - will print false
System.out.println(Arrays.equals(bytes, actuallyRead));
回答1:
Blame historical precedent. On Unix, the function used to reverse a deflate
is called inflate
. So, unlike alot of other Java IO classes, the input and output stream pair does not have (obviously) matching names.
The DeflaterOutputStream doesn't actually allow you to reverse a deflation, instead it deflates bytes as they are passed to it from a sink to a source. DeflaterInputStream also deflates, but it performs its action as data flows from the source to the sink.
In order to read your data in uncompressed (inflated) format, you need to use an InflaterInputStream
:
InflaterInputStream inputStream = new InflaterInputStream(arrayInputStream);
Also, because its possible to not get all compressed data from the stream in one read
call, you need to use a loop. Something like this:
int read;
byte[] finalBuf = new byte[0], swapBuf;
byte[] readBuffer = new byte[5012];
ByteArrayInputStream arrayInputStream = new ByteArrayInputStream(
compressed);
InflaterInputStream inputStream = new InflaterInputStream(
arrayInputStream);
while ((read = inputStream.read(readBuffer)) != -1) {
System.out.println("Intermediate read: " + read);
swapBuf = finalBuf;
finalBuf = new byte[swapBuf.length + read];
System.arraycopy(swapBuf, 0, finalBuf, 0, swapBuf.length);
System.arraycopy(readBuffer, 0, finalBuf, swapBuf.length, read);
}
Finally, make sure to either flush your deflater output stream prior to retrieving the compressed bytes (or alternatively close the stream).
回答2:
There are only 2 little changes that make your code work.
//Compress the data, and write it to somewhere (a byte array for this example)
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
DeflaterOutputStream outputStream = new DeflaterOutputStream(arrayOutputStream);
outputStream.write(bytes);
outputStream.close();
First, you have to close() the output stream. The deflator has to make some final steps to complete his work.
InflaterInputStream inputStream = new InflaterInputStream(arrayInputStream);
If you use a Deflator InputStream, you compress the compressed data again. Replace it with an Inflator InputStream and your code will work fine.
来源:https://stackoverflow.com/questions/15742087/deflatorinputstream-and-deflatoroutputstream-do-not-reconstruct-the-original-dat