8

我注意到,与使用 WinZip 等本地工具相比,Java 中的解压缩工具非常慢。

是否有更高效的 Java 第三方库可用?开源是首选。

编辑

这是使用 Java 内置解决方案与 7zip 的速度比较。我在原始解决方案中添加了缓冲输入/输出流(感谢 Jim,这确实产生了很大的不同)。

Zip 文件大小:800K Java 解决方案:2.7 秒 7Zip 解决方案:204 毫秒

下面是使用内置Java解压修改后的代码:

/** Unpacks the give zip file using the built in Java facilities for unzip. */
@SuppressWarnings("unchecked")
public final static void unpack(File zipFile, File rootDir) throws IOException
{
  ZipFile zip = new ZipFile(zipFile);
  Enumeration<ZipEntry> entries = (Enumeration<ZipEntry>) zip.entries();
  while(entries.hasMoreElements()) {
    ZipEntry entry = entries.nextElement();
    java.io.File f = new java.io.File(rootDir, entry.getName());
    if (entry.isDirectory()) { // if its a directory, create it
      continue;
    }

    if (!f.exists()) {
      f.getParentFile().mkdirs();
      f.createNewFile();
    }

    BufferedInputStream bis = new BufferedInputStream(zip.getInputStream(entry)); // get the input stream
    BufferedOutputStream bos = new BufferedOutputStream(new java.io.FileOutputStream(f));
    while (bis.available() > 0) {  // write contents of 'is' to 'fos'
      bos.write(bis.read());
    }
    bos.close();
    bis.close();
  }
}
4

3 回答 3

24

问题不在于解压缩,而是将解压缩的数据写回磁盘的效率低下。我的基准测试表明,使用

    InputStream is = zip.getInputStream(entry); // get the input stream
    OutputStream os = new java.io.FileOutputStream(f);
    byte[] buf = new byte[4096];
    int r;
    while ((r = is.read(buf)) != -1) {
      os.write(buf, 0, r);
    }
    os.close();
    is.close();

而是将方法的执行时间减少了 5 倍(对于 6 MB 的 zip 文件,从 5 秒到 1 秒)。

可能的罪魁祸首是您使用bis.available(). 除了不正确(available 返回字节数,直到调用 read 阻塞,而不是直到流结束),这绕过了 BufferedInputStream 提供的缓冲,需要对复制到输出文件的每个字节进行本机系统调用。

请注意,如果您像我上面那样使用批量读写方法,则不需要包装在 BufferedStream 中,并且关闭资源的代码不是异常安全的(如果由于任何原因读取或写入失败,isos不会关闭) )。最后,如果你在类路径中有 IOUtils,我建议使用它们经过良好测试IOUtils.copy而不是自己滚动。

于 2010-07-25T12:17:30.800 回答
3

确保在 Java 应用程序中为 unzip 方法提供 BufferedInputStream。如果您错误地使用了无缓冲的输入流,那么您的 IO 性能肯定会很糟糕。

于 2010-07-23T23:30:36.090 回答
0

I have found an 'inelegant' solution. There is an open source utility 7zip (www.7-zip.org) that is free to use. You can download the command line version (http://www.7-zip.org/download.html). 7-zip is only supported on Windows, but it looks like this has been ported to other platforms (p7zip).

Obviously this solution is not ideal since it is platform specific and relies on an executable. However, the speed compared to doing the unzip in Java is incredible.

Here is the code for the utility function that I created to interface with this utility. There is room for improvement as the code below is Windows specific.

/** Unpacks the zipfile to the output directory.  Note: this code relies on 7-zip 
   (specifically the cmd line version, 7za.exe).  The exeDir specifies the location of the 7za.exe utility. */
public static void unpack(File zipFile, File outputDir, File exeDir) throws IOException, InterruptedException
{
  if (!zipFile.exists()) throw new FileNotFoundException(zipFile.getAbsolutePath());
  if (!exeDir.exists()) throw new FileNotFoundException(exeDir.getAbsolutePath());
  if (!outputDir.exists()) outputDir.mkdirs();

  String cmd = exeDir.getAbsolutePath() + "/7za.exe -y e " + zipFile.getAbsolutePath();

  ProcessBuilder builder = new ProcessBuilder(new String[] { "cmd.exe", "/C", cmd });
  builder.directory(outputDir);
  Process p = builder.start();
  int rc = p.waitFor();
  if (rc != 0) {
    log.severe("Util::unpack() 7za process did not complete normally.  rc: " + rc);
  }
}      
于 2010-07-25T11:30:05.697 回答