Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

DeflatorInputStream and DeflatorOutputStream do not reconstruct the original data

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));
like image 796
DieterDP Avatar asked Apr 01 '13 10:04

DieterDP


1 Answers

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).

like image 68
Perception Avatar answered Oct 28 '22 03:10

Perception