使用 Java ZipOutputStream 和 BufferedOutputStream 的首选方式

Posted

技术标签:

【中文标题】使用 Java ZipOutputStream 和 BufferedOutputStream 的首选方式【英文标题】:Preferred way to use Java ZipOutputStream and BufferedOutputStream 【发布时间】:2013-01-05 21:48:20 【问题描述】:

在 Java 中,我先实例化 ZipOutputStream 还是先实例化 BufferedOutputStream 是否重要?示例:

FileOutputStream dest = new FileOutputStream(file);
ZipOutputStream zip = new ZipOutputStream(new BufferedOutputStream(dest));

// use zip output stream to write to

或者:

FileOutputStream dest = new FileOutputStream(file);
BufferedOutputStream out = new BufferedOutputStream(new ZipOutputStream(dest));

// use buffered stream to write to

在我的非科学时间安排中,我似乎无法在这里说出太多不同。我在 Java API 中看不到任何说明这些方法之一是否必要或首选的内容。有什么建议吗?似乎先压缩输出然后缓冲写入会更有效。

【问题讨论】:

理论上,压缩然后缓冲会更快。但是,GZipOutputStream 有一个内部缓冲区,因此它不会将单个字节写入底层流。根据底层流类型(例如,文件与套接字)和缓冲区的相对大小,您可能会或可能不会看到任何差异。 【参考方案1】:

您应该始终将BufferedOutputStreamZipOutputStream 包裹起来,而不是相反。见以下代码:

FileOutputStream fos = new FileOutputStream("hello-world.zip");
BufferedOutputStream bos = new BufferedOutputStream(fos);
ZipOutputStream zos = new ZipOutputStream(bos);

try 
    for (int i = 0; i < 10; i++) 
        // not available on BufferedOutputStream
        zos.putNextEntry(new ZipEntry("hello-world." + i + ".txt"));
        zos.write("Hello World!".getBytes());
        // not available on BufferedOutputStream
        zos.closeEntry();
    

finally 
    zos.close();

正如 cmets 所说,putNextEntry()closeEntry() 方法在 BufferedOutputStream 上不可用。不调用这些方法ZipOutputStream 会引发异常java.util.zip.ZipException: no current ZIP entry

为了完整起见,值得注意的是,finally 子句仅在ZipOutputStream 上调用close()。这是因为按照惯例,所有内置的 Java 输出流包装器实现都会传播关闭。

编辑

我只是反过来测试了它。事实证明,用BufferedOutputStream 包装ZipOutputStream,然后只在其上调用write()(不创建/关闭条目)不会抛出ZipException。相反,生成的 ZIP 文件将损坏,其中没有任何条目。

【讨论】:

这样的话,缓冲有什么意义吗?我不是在这里争论,只是好奇到目前为止是否有人检查过。 正如您在MrSmith42's 答案的第一部分中看到的那样,通过在写入磁盘之前缓冲已经压缩的输出流,使用内部 BufferedOutputStream 可能是有益的。您将使用更多内存(用于在刷新到磁盘之前将 zip 压缩字节保存在内存缓冲区中)但效率更高,因为磁盘 I/O 是在更大的字节块中完成的(BufferedOutputStream 的缓冲区大小已初始化与)。 ZipOutputStream 中的 buffer size BufferedOutputStream 对您来说性能最高,您应该自己 figure out,【参考方案2】:

你应该:

ZipOutputStream out =  new ZipOutputStream(new BufferedOutputStream(dest));

因为您想缓冲写入磁盘的内容(因为这在大数据块中比在许多小数据块中效率更高)。


这个

new BufferedOutputStream(new ZipOutputStream(dest));

会在 zip 压缩之前缓冲。但这一切都发生在内存中,不需要缓冲,因为很多小内存访问的速度与少数大内存访问的速度大致相同。 在内存中,所需时间通常与读取/写入的字节数成正比。

如 cmets 中所述:

不属于BufferedOutputStreamZipOutputStream 的方法也将不可用。例如。 putNextEntrycloseEntry

【讨论】:

我确信我的回答是正确的。但请随意尝试两种方式并比较性能(或调试它们)。 我的意思是,比较两者之间的任何性能是没有意义的。将ZipOutputStream 包装在BufferedOutputStream 中完全没有意义,因为它不会公开putNextEntrycloseEntry 方法。 拒绝投票作为答案并没有提到如果包装错误,ZipOutputStream 的方法在流上时不可用。

以上是关于使用 Java ZipOutputStream 和 BufferedOutputStream 的首选方式的主要内容,如果未能解决你的问题,请参考以下文章

ZipOutputStream 类的 closeEntry()

Java ??????ZipOutputStream ??????????????????

使用 ZipOutputStream 和 FileStream 压缩文件时出现异常

在 ZipOutputStream.write() 方法中强化隐私违规

java压缩文件中文名乱码问题

ZipOutputStream 在 Android 上生成损坏的 zip 文件