Compressing byte[] to byte[] with GZIPOutputStream? Unexpected end of ZLIB input stream

151 Views Asked by At

I am trying to compress and array of bytes into another array of bytes using GZIPOutputStream (in Java).

This is my code:

@Test
public void testCompressBytes() throws IOException {
    final byte[] uncompressed = RandomStringUtils.randomAlphanumeric(100000 /* 100 kb */).getBytes();

    // compress
    byte[] compressed;
    try (InputStream is = new ByteArrayInputStream(uncompressed);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            OutputStream os = new GZIPOutputStream(baos)) {
        IOUtils.copy(is, os);  // org.apache.commons.io
        os.flush();
        compressed = baos.toByteArray();
    }       
    System.out.println("Size before compression = " + uncompressed.length + ", after = " + compressed.length);

    // decompress back
    byte[] decompressedBack;
    try (InputStream is = new GZIPInputStream(new ByteArrayInputStream(compressed));
            ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
        IOUtils.copy(is, baos);  // EXCEPTION THROWN HERE
        baos.flush();
        decompressedBack = baos.toByteArray();
    }
        
    assertArrayEquals(uncompressed, decompressedBack);
}

And this is the output I'm getting:

Size before compression = 100000, after = 63920
java.io.EOFException: Unexpected end of ZLIB input stream

What could I be doing wrong?

2

There are 2 best solutions below

0
Leszek Pachura On BEST ANSWER

Thanks, everybody! Although calling GZIPOutputStream::finish() before ByteArrayOutputStream::toByteArray() seems to do the trick, I believe it's better to completely close the GZIP stream first, which in turn forces us to keep ByteArrayOutputStream outside the try-with-resources clause.

So, my reworked compression part looks like that now:

final ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (InputStream is = new ByteArrayInputStream(uncompressed);
        GZIPOutputStream gzos = new GZIPOutputStream(baos)) {
    IOUtils.copy(is, gzos);
} catch (final IOException e) {
    throw new RuntimeException(e);
}

IOUtils.closeQuietly(baos);
final byte[] compressed = baos.toByteArray();
0
Lae On

You need to call GZIPOutputStream::close before calling ByteArrayOutputStream::toByteArray, so that GZIPOutputStream writes all the end bits.

In your current code you are calling ByteArrayOutputStream::toByteArray before GZIPOutputStream::close (via try-with-resources) that's why it doesn't work.