5

我想在 Java 中创建一个 zip 存档,其中每个包含的文件都是通过序列化一些对象来生成的。我在正确关闭流时遇到问题。

代码如下所示:

try (OutputStream os = new FileOutputStream(file);
     ZipOutputStream zos = new ZipOutputStream(os);) {

  ZipEntry ze;
  ObjectOutputStream oos;

  ze = new ZipEntry("file1");
  zos.putNextEntry(ze); // start first file in zip archive
  oos = new ObjectOutputStream(zos);
  oos.writeObject(obj1a);
  oos.writeObject(obj1b);
  // I want to close oos here without closing zos
  zos.closeEntry(); // end first file in zip archive

  ze = new ZipEntry("file2");
  zos.putNextEntry(ze); // start second file in zip archive
  oos = new ObjectOutputStream(zos);
  oos.writeObject(obj2a);
  oos.writeObject(obj2b);
  // And here again
  zos.closeEntry(); // end second file in zip archive
}

我当然知道我应该在使用完每个流后关闭它,所以我应该关闭ObjectOutputStream指定位置的 s。但是,关闭ObjectOutputStreams 也会关闭ZipOutputStream我仍然需要的。

我不想省略对的调用,ObjectOutputStream.close()因为我不想依赖它目前不超过flush()and的事实reset()

我也不能使用单个ObjectOutputStream实例,因为我错过了构造函数写入的流标头(zip 存档中的每个单个文件都不是完整的对象序列化文件,我无法独立反序列化它们)。

再次读取文件时会出现同样的问题。

我看到的唯一方法是将其包装ZipOutputStream在某种“CloseProtectionOutputStream”中,它将转发所有方法,除非close()在将其提供给ObjectOutputStream. 但是,这似乎相当 hacky,我想知道我是否错过了 API 中更好的解决方案。

4

3 回答 3

2

在这种情况下,出于您提到的所有原因,我根本不会将 myObjectOutputStream传输到ZipOutputStream. 相反,序列化为 abyte[]然后将其直接写入ZipOutputStream. 这样,您可以自由关闭,ObjectOutputStream并且您生成的每个byte[]都将具有来自序列化程序的正确标头。不利的一面是,您最终会得到一个byte[]以前没有的内存,但是如果您立即摆脱它,假设我们不是在谈论数百万个对象,那么垃圾收集器应该不会很难清理向上。

就我的两分钱...

它至少听起来不像改变close()行为的流子类那么难懂。

于 2013-04-10T12:07:43.180 回答
2

如果您无论如何都打算丢弃 ObjectOutputStream ,那么调用flush()而不是应该就足够了close(),但是正如您在问题中所说,最安全的方法可能是在底层 ZipOutputStream 周围使用包装器来阻止close()调用。Apache commons-io 有CloseShieldOutputStream用于此目的。

于 2013-04-10T12:35:21.267 回答
2

如果您的OutputStream包装器在多次关闭时引发异常,则它不是 hack。您可以为每个 zip 条目创建一个包装器。

从架构的角度来看,我认为ObjectOutputStream作者应该提供一个禁用close()级联的选项。您只是在解决他缺少的 API。

于 2013-04-10T12:38:27.467 回答