Что такое хорошая библиотека Java для архивирования / распаковки файлов? [закрыто]

232

Я посмотрел библиотеку Zip по умолчанию, которая поставляется с JDK и библиотеками сжатия Apache, и я недоволен ими по 3 причинам:

  1. Они раздуты и имеют плохой дизайн API. Мне нужно написать 50 строк выходного массива байтовых пластин, ввести данные в формате zip, отфильтровать потоки и закрыть соответствующие потоки, перехватить исключения и самостоятельно переместить байтовые буферы ? Почему у меня не может быть простого API, который выглядит так Zipper.unzip(InputStream zipFile, File targetDirectory, String password = null)и Zipper.zip(File targetDirectory, String password = null)работает?

  2. Кажется, что сжатие, разархивирование уничтожает метаданные файла, а обработка пароля нарушается.

  3. Кроме того, все библиотеки, которые я пробовал, были в 2-3 раза медленнее по сравнению с инструментами zip из командной строки, которые я получаю с UNIX?

Для меня (2) и (3) - второстепенные моменты, но я действительно хочу хорошую проверенную библиотеку с однострочным интерфейсом.

pathikrit
источник
13
Что касается # 1, это потому, что не все просто разархивируют файл в каталог. Если вы всегда используете один и тот же шаблон, почему бы просто не написать вспомогательный класс, который обернет один из других и сделает то, что вам нужно, и просто использовать это ?
Эдвард Томсон
14
@EdwardThomson, потому что проще использовать библиотеку, чем писать код, тестировать код и поддерживать код.
Зак
11
@EdwardThomson: Ваш аргумент неверен. Посмотрите на Python zip API: docs.python.org/3/library/zipfile . Вам нужна 1 строка кода, чтобы сжать или разархивировать файлы. API должны хорошо обрабатывать общий случай, и я не могу придумать ни одного варианта использования zip API, кроме zip-архивирования или zip-архивирования.
патикрит
7
@wrick: сжать файл или распаковка файла является частным случаем сжать или разархивацию потока. Если ваш API не позволяет мне записывать поток в него, а вместо этого заставляет меня записывать поток в файл только для того, чтобы я мог передать его в ваш API, тогда ваш API поврежден мозгом.
Эдвард Томсон
52
@EdwardThomson - Отлично, поэтому заставьте библиотеку поддерживать как файлы, так и потоки. Это пустая трата времени каждого - моего, вашего, спрашивающего и всех других Googlers, которые наткнуться на это, что каждый из нас должен внедрить наши собственные утилиты Zip. Точно так же, как есть СУХОЙ, есть КАЧЕСТВО - Не повторяйте других людей.
ArtOfWarfare

Ответы:

291

Я знаю, что уже поздно, и есть много ответов, но этот zip4j - одна из лучших библиотек для архивирования, которые я использовал. Это просто (без кода котла) и может легко обрабатывать файлы, защищенные паролем.

import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.core.ZipFile;


public static void unzip(){
    String source = "some/compressed/file.zip";
    String destination = "some/destination/folder";
    String password = "password";

    try {
         ZipFile zipFile = new ZipFile(source);
         if (zipFile.isEncrypted()) {
            zipFile.setPassword(password);
         }
         zipFile.extractAll(destination);
    } catch (ZipException e) {
        e.printStackTrace();
    }
}

Зависимость Maven это:

<dependency>
    <groupId>net.lingala.zip4j</groupId>
    <artifactId>zip4j</artifactId>
    <version>1.3.2</version>
</dependency>
user2003470
источник
1
я получил org.zeroturnaround.zip.ZipException: java.io.FileNotFoundException: images \ 001GL.JPG: открыть не удалось: ошибка EINVAL (неверный аргумент)
Smit Patel
4
Работает ли это с Android?
Эркан
1
Нет, он не работает нормально с Android, он не поддерживает китайский язык.
Дирадж Химани
3
Zip4J не поддерживает чтение zip из входного потока, только с диска.
Рено Серрато
2
Веб-сайт не имеет Javadoc.
JohnC
76

С Apache Commons-IO «S IOUtilsвы можете сделать это:

try (java.util.zip.ZipFile zipFile = new ZipFile(file)) {
  Enumeration<? extends ZipEntry> entries = zipFile.entries();
  while (entries.hasMoreElements()) {
    ZipEntry entry = entries.nextElement();
    File entryDestination = new File(outputDir,  entry.getName());
    if (entry.isDirectory()) {
        entryDestination.mkdirs();
    } else {
        entryDestination.getParentFile().mkdirs();
        try (InputStream in = zipFile.getInputStream(entry);
             OutputStream out = new FileOutputStream(entryDestination)) {
            IOUtils.copy(in, out);
        }
    }
  }
}

Это все еще некоторый шаблонный код, но у него есть только 1 не экзотическая зависимость: Commons-IO

Джеффри Де Смет
источник
1
@VitalySazanovich Вы имеете в виду Java 7 ZipEntry.
Рэнди
2
Спасибо. Также необходимо zipFile.close () в конце.
JoshuaD
4
почему не IOUtils.closeQuietly (out)?
Хуан Мендес
2
@JuanMendez, потому что если при закрытии возникают ошибки, вы не можете быть уверены, что файл был сохранен полностью и правильно. Но вдобавок к нормальному close()это не повредит.
vadipp
3
Это решение уязвимо для ZipSlip (также затрагивается
zip4j
40

Извлеките zip-файл и все его подпапки, используя только JDK:

private void extractFolder(String zipFile,String extractFolder) 
{
    try
    {
        int BUFFER = 2048;
        File file = new File(zipFile);

        ZipFile zip = new ZipFile(file);
        String newPath = extractFolder;

        new File(newPath).mkdir();
        Enumeration zipFileEntries = zip.entries();

        // Process each entry
        while (zipFileEntries.hasMoreElements())
        {
            // grab a zip file entry
            ZipEntry entry = (ZipEntry) zipFileEntries.nextElement();
            String currentEntry = entry.getName();

            File destFile = new File(newPath, currentEntry);
            //destFile = new File(newPath, destFile.getName());
            File destinationParent = destFile.getParentFile();

            // create the parent directory structure if needed
            destinationParent.mkdirs();

            if (!entry.isDirectory())
            {
                BufferedInputStream is = new BufferedInputStream(zip
                .getInputStream(entry));
                int currentByte;
                // establish buffer for writing file
                byte data[] = new byte[BUFFER];

                // write the current file to disk
                FileOutputStream fos = new FileOutputStream(destFile);
                BufferedOutputStream dest = new BufferedOutputStream(fos,
                BUFFER);

                // read and write until last byte is encountered
                while ((currentByte = is.read(data, 0, BUFFER)) != -1) {
                    dest.write(data, 0, currentByte);
                }
                dest.flush();
                dest.close();
                is.close();
            }


        }
    }
    catch (Exception e) 
    {
        Log("ERROR: "+e.getMessage());
    }

}

Zip-файлы и все их подпапки:

 private void addFolderToZip(File folder, ZipOutputStream zip, String baseName) throws IOException {
    File[] files = folder.listFiles();
    for (File file : files) {
        if (file.isDirectory()) {
            addFolderToZip(file, zip, baseName);
        } else {
            String name = file.getAbsolutePath().substring(baseName.length());
            ZipEntry zipEntry = new ZipEntry(name);
            zip.putNextEntry(zipEntry);
            IOUtils.copy(new FileInputStream(file), zip);
            zip.closeEntry();
        }
    }
}
Башир Бейкзаде
источник
7
Призывы к закрытию должны быть как минимум внутри «окончательных» блоков. Исключения не обрабатываются хорошо. -> Я думаю, это часть того, почему ОП попросил использовать библиотеку .
4
Это слишком много кода. Это можно сделать в 2 строки.
Макки
/mnt/sdcard/final_unzip_data/Product_images\001GL.JPG: открытие не удалось: EINVAL (неверный аргумент)
Смит Патель,
@Joe Michael Спасибо, приятель, что опубликовал это. Это решает мою проблему. Я дам тебе +1 заextractFolder(String zipFile,String extractFolder)
OO7
Этот код не сохраняет атрибуты и разрешения файлов ... если вы используете что-то подобное для разархивирования работающего приложения, будьте готовы к странным ошибкам, связанным с разрешениями файлов. Это стоило мне недели головной боли.
Ренато
23

Другой вариант, который вы можете проверить, это zt-zip, доступный на Maven central и на странице проекта по адресу https://github.com/zeroturnaround/zt-zip.

Он имеет стандартные функции упаковки и распаковки (в потоках и в файловой системе) + множество вспомогательных методов для проверки файлов в архиве или добавления / удаления записей.

toomasr
источник
17

Полная реализация для Zip / Распаковать папку / файл с zip4j


Загрузите банку отсюда и добавьте ее в путь сборки вашего проекта. classНиже может сжимать и извлекать любой файл или папку, с или без пароля охрано

import java.io.File;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;
import net.lingala.zip4j.core.ZipFile;  

public class Compressor {
    public static void zip(String targetPath, String destinationFilePath, String password) {
        try {
            ZipParameters parameters = new ZipParameters();
            parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE);
            parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);

            if(password.length()>0){
                parameters.setEncryptFiles(true);
                parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES);
                parameters.setAesKeyStrength(Zip4jConstants.AES_STRENGTH_256);
                parameters.setPassword(password);
            }

            ZipFile zipFile = new ZipFile(destinationFilePath);

            File targetFile = new File(targetPath);
            if(targetFile.isFile()){
                zipFile.addFile(targetFile, parameters);
            }else if(targetFile.isDirectory()){
                zipFile.addFolder(targetFile, parameters);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void unzip(String targetZipFilePath, String destinationFolderPath, String password) {
        try {
            ZipFile zipFile = new ZipFile(targetZipFilePath);
            if (zipFile.isEncrypted()) {
                zipFile.setPassword(password);
            }
            zipFile.extractAll(destinationFolderPath);

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**/ /// for test only
    public static void main(String[] args) {

        String targetPath = "target\\file\\or\\folder\\path";
        String zipFilePath = "zip\\file\\Path"; 
        String unzippedFolderPath = "destination\\folder\\path";
        String password = "your_password"; // keep it EMPTY<""> for applying no password protection

        Compressor.zip(targetPath, zipFilePath, password);
        Compressor.unzip(zipFilePath, unzippedFolderPath, password);
    }/**/
}
Минхас Камаль
источник
1
Хороший ответ и библиотека. Извлечение 1868 файлов заняло ~ 15 секунд в этой библиотеке, по сравнению с 20+ минутами при использовании ZipInputStream (по некоторым причинам)
Jonty800
8

Очень хороший проект - TrueZip .

TrueZIP - это основанная на Java инфраструктура плагинов для виртуальных файловых систем (VFS), которая обеспечивает прозрачный доступ к архивным файлам, как если бы они были просто каталогами

Например (с сайта ):

File file = new TFile("archive.tar.gz/README.TXT");
OutputStream out = new TFileOutputStream(file);
try {
   // Write archive entry contents here.
   ...
} finally {
   out.close();
}
Майкл
источник
Библиотека выглядит красиво - все еще не очевидно, как просто распаковать zip-файл, используя zipinputstream / file / path.
патикрит
1
TrueZIP не очень хорошо справляется с чтением из потоков.
Тео Клеструп Ройеззон
5
Разве это не то же самое, что вы можете сделать в Java 7? (посмотрите на ZipFileSystemProvider ).
Петр
1
@peterh: стандартный JDK ZipFileSystemProvider был бы хорошим ответом. Только немногие видят это как комментарий.
iuzuz
3

Другой вариант - JZlib . По моему опыту, он менее «центрирован на файлах», чем zip4J, поэтому, если вам нужно работать с каплями в памяти, а не с файлами, вы можете взглянуть на это.

Хенрик Аастед Серенсен
источник
0

Вы смотрели на http://commons.apache.org/vfs/ ? Он утверждает, что многое упростит для вас. Но я никогда не использовал это в проекте.

Я также не знаю других библиотек сжатия Java-Native, кроме JDK или Apache Compression.

Я помню, как однажды мы вынули некоторые функции из Apache Ant - в них встроено множество утилит для сжатия / распаковки.

Пример кода с VFS будет выглядеть так:

File zipFile = ...;
File outputDir = ...;
FileSystemManager fsm = VFS.getManager();
URI zip = zipFile.toURI();
FileObject packFileObject = fsm.resolveFile(packLocation.toString());
FileObject to = fsm.toFileObject(destDir);
FileObject zipFS;
try {
    zipFS = fsm.createFileSystem(packFileObject);
    fsm.toFileObject(outputDir).copyFrom(zipFS, new AllFileSelector());
} finally {
    zipFS.close();
}
wemu
источник
1
Похоже, что поддержка zip-файлов в VFS сама по себе довольно ограничена: commons.apache.org/vfs/filesystems.html
TJ Crowder