1

I am trying to read the contents of byte array created by gzipping an object, however when I try to unzip the contents of the byte array I get an error stating : java.io.EOFException: Unexpected end of ZLIB input stream

I am trying to use the try-with-resources idiom of java to manage the stream resources while compressing and decompressing an object, however if I add the ByteArrayOutputStream to the try-with-resources block another stream ObjectOutputStream fails to close.

I checked the implementation of ByteArrayOutputStream it extends AutoCloseable interface and even though its close isn't performing any work as such, it shouldn't have interfered with the closing of other resources.

Code I tried for compressing the object:

    //This fails
    public static byte[] returnCompressedObject(Object object) {
        try(ByteArrayOutputStream out = new ByteArrayOutputStream();
            GZIPOutputStream gzOut = new GZIPOutputStream(out);
            ObjectOutputStream objOut = new ObjectOutputStream(gzOut)) {
            objOut.writeObject(object);
            return out.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException("Error converting object to bytes ", e);
        }
    }

    //This works with ByteArrayOutputStream moved out of try-with-resources block
    public static byte[] returnCompressedObjectWithByteArrayOutside(Object object) {
        //ByteArrayOutputStream moved out of try-with-resources block
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        try(GZIPOutputStream gzOut = new GZIPOutputStream(out);
            ObjectOutputStream objOut = new ObjectOutputStream(gzOut)) {
            objOut.writeObject(object);
        } catch (IOException e) {
            throw new RuntimeException("Error converting object to bytes ", e);
        }
        return out.toByteArray();
    }

    //This works with explicit close being called on ObjectOutputStream
    public static byte[] returnCompressedObjectWithExplicitClose(Object object) {
        try(ByteArrayOutputStream out = new ByteArrayOutputStream();
            GZIPOutputStream gzOut = new GZIPOutputStream(out);
            ObjectOutputStream objOut = new ObjectOutputStream(gzOut)) {
            objOut.writeObject(object);
            //Explicit close being called on ObjectOutputStream
            objOut.close();
            return out.toByteArray();
        } catch (IOException e) {
            throw new RuntimeException("Error converting object to bytes ", e);
        }

    }

Out of these returnCompressedObjectWithByteArrayOutside and returnCompressedObjectWithExplicitClose seem to return a byte array that I am able to use with below unzip method, but using returnCompressedObject before unzip ends up throwing exception java.io.EOFException: Unexpected end of ZLIB input stream during unzip Method used for unzipping:

    public static String decompressUsingGZip(byte[] object) {
        try(ByteArrayInputStream messageInputStream = new ByteArrayInputStream(object);
            GZIPInputStream gzipStream = new GZIPInputStream(messageInputStream);
            InputStreamReader inputStreamReader = new InputStreamReader(gzipStream, StandardCharsets.UTF_8);
            BufferedReader reader = new BufferedReader(inputStreamReader)){
            return reader.lines().collect(Collectors.joining());
        } catch (IOException e) {
            throw new RuntimeException("Error uncompressing object", e);
        }
    }

I am trying to understand why the returnCompressedObject isn't behaving as it should and closing the streams.

On searching the documentation I didn't find any place where it is mentioned that we shouldn't use ByteArrayOutputStream in try-with-resources.

Any help in understanding the cause of this behaviour is appreciated.

Dhairya Seth
  • 779
  • 3
  • 7
  • 15

2 Answers2

1

I am trying to understand why the returnCompressedObject isn't behaving as it should and closing the streams.

It fails because the GZIPOutputStream has internal buffer, which is not flushed. By closing it, you are forcing the stream to do any final operations and write out any remaining bytes. If you do not close it, you'll lose some data, hence the Unexpected end of ZLIB input stream message.

Svetlin Zarev
  • 14,713
  • 4
  • 53
  • 82
1

When you are converting OutputStream to byteArray it expects it to be closed. In your 3rd case you are explicitly closing ObjectOutputStream before calling out.toByteArray() which makes complete sense. In your 2nd case you've moved out.toByteArray() outside try-catch and defined ObjectOutputStream in try-with-resource block which closes ObjectOutputStream when try blocks ended. So when you are calling out.toByteArray(), ObjectOutputStream was already closed hence no issues again.

I will still suggest you to take either 3rd approach or wrap entire code in another try-catch block and define ByteArrayOutputStream within try-with-resource block. So that this steam will close while returning.