java.io.EOFException:在try-with-resources中使用ByteArrayOutputStream时ZLIB输入流的意外结束

时间:2019-06-05 09:33:16

标签: java objectoutputstream try-with-resources bytearrayoutputstream

我正在尝试读取通过gziping对象创建的字节数组的内容,但是当我尝试解压缩字节数组的内容时,出现错误,指出:java.io.EOFException:ZLIB输入流的意外结束< / p>

我正在尝试使用Java的try-with-resources惯用法来压缩和解压缩对象时管理流资源,但是,如果我将ByteArrayOutputStream添加到try-with-resources块中,则另一个流ObjectOutputStream无法关闭。

我检查了ByteArrayOutputStream的实现,该实现扩展了AutoCloseable接口,即使它的关闭未执行任何工作,它也不应干扰其他资源的关闭。

我尝试压缩对象的代码:

    //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);
        }

    }
在这些 returnCompressedObjectWithByteArrayOutside returnCompressedObjectWithExplicitClose 中,

似乎返回了一个我可以在以下unzip方法中使用的字节数组,但是使用了 returnCompressedObject 解压缩结束前引发异常 java.io.EOFException:解压缩期间ZLIB输入流的意外结束 用于解压缩的方法:

    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);
        }
    }

我试图理解为什么 returnCompressedObject 的行为不正常,并关闭流。

在搜索文档时,我没有发现任何地方提到我们不应该在try-with-resources中使用ByteArrayOutputStream。

我们将帮助您理解这种行为的原因。

2 个答案:

答案 0 :(得分:1)

  

我试图了解为什么returnCompressedObject不是   保持应有的状态并关闭流。

它失败,因为GZIPOutputStream有内部缓冲区,没有刷新。通过关闭它,您将强制流执行任何最终操作并写出所有剩余字节。如果不关闭它,将会丢失一些数据,因此会丢失Unexpected end of ZLIB input stream消息。

答案 1 :(得分:1)

将OutputStream转换为byteArray时,它期望将其关闭。 在第3种情况下,您将在调用 out.toByteArray()之前显式关闭 ObjectOutputStream ,这完全有意义。 在第二种情况下,您已经将 out.toByteArray()移到了try-catch之外,并在try-with-resource块中定义了 ObjectOutputStream ,当try块结束时,它将关闭ObjectOutputStream。因此,当您调用out.toByteArray()时,ObjectOutputStream已关闭,因此不会再出现问题。

我仍然建议您采用第三种方法,或将整个代码包装在另一个try-catch块中,并在try-with-resource块中定义ByteArrayOutputStream。这样蒸汽就可以在返回时关闭。