Низкая производительность Java разархивировала утилиты

Я заметил, что разархивировать средство в Java является чрезвычайно медленным по сравнению с использованием собственного инструмента, такого как WinZip.

Существует ли сторонняя библиотека, доступная для Java, который более эффективен? Открытый исходный код предпочтен.

Править

Вот сравнение скорости с помощью Java встроенное решение по сравнению с 7zip. Я добавил, потоки ввода-вывода с буферизацией в моем исходном решении (благодарит Jim, это действительно имело большое значение).

Размер zip-файла: Решение для Java 800K: 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();
  }
}
8
задан meriton - on strike 25 July 2010 в 12:27
поделиться

3 ответа

Проблема не в разархивировании, а в неэффективном способе записи разархивированных данных обратно на диск. Мои тесты показывают, что использование

    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 раз (с 5 до 1 секунды для zip-файла размером 6 МБ).

Вероятная виновница - использование вами bis.available () . Помимо того, что он неверен (available возвращает количество байтов до тех пор, пока вызов для чтения не заблокируется, а не до конца потока), это обходит буферизацию, предоставляемую BufferedInputStream, требуя собственного системного вызова для каждого байта, скопированного в выходной файл.

Обратите внимание, что обертывание в BufferedStream не требуется, если вы используете методы массового чтения и записи, как я делаю выше, и что код для закрытия ресурсов не является безопасным для исключений (если чтение или запись не удается по какой-либо причине, ни - это , ни os не будет закрыта). Наконец, если у вас есть IOUtils в пути к классам, я рекомендую использовать их хорошо протестированные IOUtils.copy вместо того, чтобы катить свои собственные.

22
ответ дан 5 December 2019 в 06:52
поделиться

Я нашел «неизящное» решение. Существует бесплатная утилита с открытым исходным кодом 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);
  }
}      
-1
ответ дан 5 December 2019 в 06:52
поделиться

Убедитесь, что вы передаете методу unzip поток BufferedInputStream в вашем Java-приложении. Если вы допустили ошибку, используя небуферизованный входной поток, ваша производительность ввода-вывода гарантированно снизится.

3
ответ дан 5 December 2019 в 06:52
поделиться
Другие вопросы по тегам:

Похожие вопросы: