8

Java の解凍機能は、WinZip などのネイティブ ツールを使用する場合に比べて非常に遅いことに気付きました。

より効率的な 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 分の 1 に短縮されます (6 MB の zip ファイルの場合、5 秒から 1 秒に短縮されます)。

可能性のある原因は、の使用ですbis.available()。正しくない (available は、ストリームの最後までではなく、read の呼び出しがブロックされるまでのバイト数を返す) ことは別として、これは BufferedInputStream によって提供されるバッファリングをバイパスし、出力ファイルにコピーされるすべてのバイトに対してネイティブ システム コールを必要とします。

上記のように一括読み取りおよび書き込みメソッドを使用する場合、BufferedStream でラップする必要はなく、リソースを閉じるコードは例外セーフではないことに注意してください (何らかの理由で読み取りまたは書き込みが失敗した場合、どちらisos閉じられません。 )。最後に、クラス パスに IOUtil がある場合は、IOUtils.copy独自に作成するのではなく、十分にテストされた IOUtil を使用することをお勧めします。

于 2010-07-25T12:17:30.800 に答える
3

Java アプリケーションで unzip メソッドに BufferedInputStream を供給していることを確認してください。バッファリングされていない入力ストリームを使用するというミスを犯した場合、IO パフォーマンスは確実に低下します。

于 2010-07-23T23:30:36.090 に答える
0

「洗練されていない」解決策を見つけました。無料で使用できるオープン ソース ユーティリティ 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);
  }
}      
于 2010-07-25T11:30:05.697 に答える