【问题标题】:Poor Performance of Java's unzip utilitiesJava 的解压缩实用程序性能不佳
【发布时间】:2010-07-23 19:49:02
【问题描述】:

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

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

编辑

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

压缩文件大小: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();
  }
}

【问题讨论】:

  • 我的解压缩功能没有问题,我一直在处理 250 MB 的 zip 文件,其中包含压缩文本文件。你在做什么,要花这么长时间?是不是很复杂?
  • 也许你是在低优先级线程中做的?
  • 我强烈推荐:if( entry.getName().contains("..") ) continue;
  • 这个问题的答案是有用的,但有用性与问题无关(因为问题本身不正确)。我很想将问题更改为“为什么我的解压缩 Java 代码这么慢”以帮助未来的搜索者......不过,最佳答案非常好。我去换个问题对我来说是个坏主意吗?

标签: java unzip


【解决方案1】:

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

    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 中,并且关闭资源的代码不是异常安全的(如果由于任何原因读取或写入失败,@ 987654324@ 和 os 将被关闭)。最后,如果你在类路径中有 IOUtils,我建议使用他们经过良好测试的 IOUtils.copy 而不是自己滚动。

【讨论】:

  • 感谢 Meriton!我试过这个,性能现在可以和 7zip 媲美。我已将 IOUtils 添加到我的工具箱中以备将来使用。这是一个很好的建议。
【解决方案2】:

我找到了一个“不优雅”的解决方案。有一个免费使用的开源实用程序 7zip (www.7-zip.org)。您可以下载命令行版本(http://www.7-zip.org/download.html)。 7-zip 仅在 Windows 上受支持,但看起来它已被移植到其他平台(p7zip)。

显然这种解决方案并不理想,因为它是特定于平台的并且依赖于可执行文件。但是,与在 Java 中解压缩相比,它的速度令人难以置信。

这是我创建的用于与该实用程序交互的实用程序函数的代码。由于以下代码是特定于 Windows 的,因此还有改进的余地。

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

【讨论】:

    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多