Яка хороша бібліотека 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 рази повільнішими порівняно з поштовими інструментами командного рядка, які я отримую з UNIX?

Для мене (2) і (3) - це незначні моменти, але я дуже хочу добре перевірену бібліотеку з однорядним інтерфейсом.


13
Що стосується №1, то це тому, що не всі просто розпаковують файл до каталогу. Якщо ви завжди використовуєте один і той же візерунок, чому б просто не написати клас утиліти, який обгортає один з інших і робить те, що вам потрібно, і просто використовувати це ?
Едвард Томсон

14
@EdwardThomson, тому що простіше користуватися бібліотекою, ніж писати код, тестовий код та підтримувати код.
Зак

11
@EdwardThomson: Ваш аргумент недійсний. Подивіться на zip API Python: docs.python.org/3/library/zipfile . Вам потрібно 1 рядок коду, щоб зібрати або розпакувати файли. API повинні дуже добре поводитися з загальним випадком, і я не можу придумати жодного випадку використання zip API, окрім зашпаровування чи розпакування.
pathikrit

7
@wrick: стискання файлу або розпакування файлу - це особливий випадок, який можна застебнути або розпакувати. Якщо ваш API не дозволяє мені записувати потік до нього, а замість цього змушує мене написати потік у файл просто так, щоб я міг подати його вашому API, тоді ваш API пошкоджений мозку.
Едвард Томсон

52
@EdwardThomson - прекрасно, тому змушуйте бібліотеку підтримувати і файли, і потоки. Це марний час кожного - мого, вашого, запитувача та всіх інших службовців Google, які наткнуться на це, що всі ми повинні реалізувати власні Zip Utilities. Подібно до того, як є СУХА, є і ДРОП - Не повторюйте інших людей.
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();
    }
}

Залежність Мейвена:

<dependency>
    <groupId>net.lingala.zip4j</groupId>
    <artifactId>zip4j</artifactId>
    <version>1.3.2</version>
</dependency>

1
я отримав org.zeroturnaround.zip.ZipException: java.io.FileNotFoundException: images \ 001GL.JPG: не вдалося відкрити: помилка EINVAL (невірний аргумент)
Smit Patel

4
Це працює з Android?
Еркан

1
Ні, він не працює з Android, він не підтримує китайську мову.
Dhiraj Himani

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 () наприкінці.
ДжошуаD

4
чому б не IOUtils.closeQuietly (out)?
Хуан Мендес

2
@JuanMendez, тому що при закритті є помилки, ви не можете бути впевнені, що файл збережено повністю та правильно. Але крім нормального close()це не зашкодить.
vadipp

3
Це рішення є вразливим для ZipSlip (також впливає
zip4j

40

Витягніть поштовий файл та всі його папки, використовуючи лише 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 (недійсний аргумент)
Smit Patel

@Joe Michael Дякую приятелю за повідомлення про це. Це вирішує мою проблему. Я дам у +1 заextractFolder(String zipFile,String extractFolder)
OO7

Цей код не зберігає атрибутів та дозволів файлів ... якщо ви використовуєте щось подібне для розпакування запущеного додатку, будьте готові до дивних помилок щодо дозволів файлів. Це коштувало мені тижня головного болю.
Ренато

23

Ще один варіант, який ви можете перевірити, - це zt-zip, доступний на центральній сторінці Maven та на сторінці проекту за посиланням https://github.com/zeroturnaround/zt-zip

Він має стандартну функцію упаковки та розпакування (на потоках та у файловій системі) + безліч допоміжних методів для тестування файлів в архіві або додавання / видалення записів.


17

Повна реалізація для завантаження / розпакування папки / файлу за допомогою 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.
pathikrit

1
TrueZIP, здається, не дуже добре справляється з читанням із потоків.
Teo Klestrup Röijezon

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

1
Схоже, підтримка zip-файлів у файлах VFS сама по собі є досить обмеженою: commons.apache.org/vfs/filesystems.html
TJ Crowder
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.