2010-07-23 3 views
8

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

Есть ли доступная сторонняя библиотека для Java, которая более эффективна? Предпочитаемый вариант с открытым исходным кодом.

Редактировать

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

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(); 
    } 
} 
+1

У меня не было проблем с функциями unzip, и я обрабатывал ZIP-файлы размером 250 МБ, которые содержат текстовые файлы с gzipped. Что вы делаете, так долго? Это что-то сложное? –

+0

Возможно, если вы делаете это в потоке с низким приоритетом? –

+0

Я настоятельно рекомендую: 'if (entry.getName(). Содержит (" ..")) continue;' –

ответ

3

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

-1

Я нашел «неэлегантное» решение. Существует бесплатная утилита с открытым исходным кодом 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

-1 для мощной дешевой копии. –

20

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

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 второе для архива 6 МБ).

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

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

+1

Спасибо Meriton! Я пробовал это и производительность теперь сопоставимы с 7zip. Я добавил IOUtils в свой инструментарий для будущего. Это очень хорошее предложение. – Tony

+0

@tony Почему вы не приняли ответ на меритоны? – t0r0X

Смежные вопросы