0

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?

Leszek Pachura
  • 403
  • 4
  • 18
  • 1
    `((GZIPOutputStream)os).finish();` is what you need – g00se Jan 28 '23 at 14:13
  • @g00se - Hmmm, here they say that close() automatically called by the try-with-resources clause should be enough? https://stackoverflow.com/questions/29172953/do-i-need-to-call-finish-when-using-a-gzipoutputstream-object-inside-try-wtth – Leszek Pachura Jan 28 '23 at 19:38
  • Well it wasn't enough for me;) – g00se Jan 28 '23 at 19:43

2 Answers2

1

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.

Lae
  • 589
  • 1
  • 5
0

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();
Leszek Pachura
  • 403
  • 4
  • 18