在Java中处理Zip和GZip文件

17

我已经有一段时间没有使用Java I/O了,也不清楚如何最好地处理Zip和GZip文件。 我不需要完整的演示实例 - 我主要是想知道正确的接口和方法。 是的,我可以查找任意的教程,但性能可能会有影响(这些文件可能很大),我关心的是使用最佳工具来完成工作。

我将要实现的基本过程:

  • 下载一堆文件(可能是压缩的或gzip的,也可能都有)到一个临时文件夹中。
  • 将所有提取后的文件添加到一个临时文件夹中的新zip文件中。

输入文件可能被压缩并进行多次归档。例如,“全面提取”应该获取以下任何输入(我不能控制这些),并留下 foo.txt

  • foo.txt.gz
  • foo.txt.zip
  • foo.txt.gz.zip
  • foo.txt.zip.gz
  • ...
  • foo.txt.gz.gz.gz.zip.gz.zip.zip.gz.gz
  • ...

然后,我可能会剩下 foo.txt bar.mp3 baz.exe - 所以我只需将它们全部添加到一个新的zip文件中,文件名为一些通用名称。

问题:

  • 考虑到文件大小可能是个问题,我应该使用哪些(接口/类/方法)来快速:
    • 提取zip文件?
    • 提取gzip文件?
    • 写入zip文件?
  • 保留单独提取的文件在内存中,再写回磁盘,这样做是否更好?还是,
  • 潜在大文件是否使得这不是一个好主意?
3个回答

11

不要把所有未压缩的数据都保存在内存中,否则可能会耗尽堆空间。当解压缩时,您需要将数据流式传输到文件中,然后在需要创建最终zip文件时从文件中流回来。

我以前没有处理过压缩文件,但这里有一个示例,展示了如何解压缩一个经过gzip压缩的文件:

import java.io.*;
import java.util.zip.*;

//unzipping a gzipped file
GZIPInputStream in = null;
OutputStream out = null;
try {
   in = new GZIPInputStream(new FileInputStream("file.txt.gz"));
   out = new FileOutputStream("file.txt");
   byte[] buf = new byte[1024 * 4];
   int len;
   while ((len = in.read(buf)) > 0) {
       out.write(buf, 0, len);
   }
}
catch (IOException e) {
   e.printStackTrace();
}
finally {
   if (in != null)
       try {
           in.close();
       }
       catch (IOException ignore) {
       }
   if (out != null)
       try {
           out.close();
       }
       catch (IOException ignore) {
       }
}

@StanislavPalatnik 可能是代码中与问题无关的部分造成的原因 ;) (顺便说一句,我不是一个downvoter)catch (IOException e) { e.printStackTrace(); } finally { if (in != null) try { in.close(); } catch (IOException ignore) { } if (out != null) try { out.close(); } catch (IOException ignore) { } } - Karussell
使用Java 9,可以简化为以下代码:try (InputStream in = new GZIPInputStream(new FileInputStream("file.txt.gz")); OutputStream out = new FileOutputStream("file.txt")) { in.transferTo(out); } - Martin

8
注意,下面建议使用的库TrueZip已被TrueVFS取代。
我发现TrueZIP库很有用。它允许您将存档文件视为另一个文件系统并使用熟悉的Java I/O API。
java.util.zip API不同,TrueZIP提供对存档内容的随机访问,因此文件大小不应成为问题。如果我记得正确,它会检测存档文件并在将它们放入存档时不会试图冗余压缩它们。
引用TrueZIP页面的话:
TrueZIP API提供了著名类File、FileInputStream和FileOutputStream的即插即用替代品。这种设计使得TrueZIP非常容易使用:只需为包de.schlichtherle.io添加几个导入语句并在必要时添加一些类型转换,就可以使大多数客户端应用程序支持归档功能。
现在,您可以像操作路径名中的目录一样简单地处理归档文件。例如,路径名archive.zip/readme表示ZIP文件archive.zip中的归档条目readme。请注意,文件名后缀是完全可配置的,并且TrueZIP会自动检测错误的情况并将其恢复为普通文件或目录。这种递归工作方式,因此归档文件甚至可以被封装在另一个归档文件中,如outer.zip/inner.zip/readme。

谢谢。我最终选择了TrueZIP,虽然一开始有点困难(因为不太理解它),但现在它运行得非常好。 - Matt Ball
顺便提一下,java.util.ZipFile也提供了内存映射的随机访问:http://java.sun.com/developer/technicalArticles/Programming/compression/ - reve_etrange
@reve_etrange,您介意更新链接或解释一下您的意思吗? - Karussell
http://web.archive.org/web/20101026174612/http://java.sun.com/developer/technicalArticles/Programming/compression/ - reve_etrange
顺便提一下,这篇文章仍然可以在Oracle的网站上找到:http://www.oracle.com/technetwork/articles/java/compress-1565076.html - blong

3

可能有一个库能够让这件事情变得容易。

但是,如果没有,您仍然可以通过 java.util.zip classes 来进行艰难的操作... 使用 ZipFileZipInputStream,以及 ZipEntry 用于 zip。

GZIPInputStream 可以包装 FileInputStream 以进行 gzip,需要注意的是 gzip 仅适用于单个文件。

这两种类型的 InputStreams 也有各自的 OutputStreams。

很遗憾,虽然我知道这些类,但我从未真正使用过它们,所以我无法给出更多建议。
编辑:Zip函数似乎没有任何方法可以向zip文件中添加新文件而不重新创建整个文件。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接