Was ist eine gute Java-Bibliothek zum Komprimieren / Entpacken von Dateien? [geschlossen]

231

Ich habe mir die Standard-Zip-Bibliothek angesehen, die mit dem JDK und den Apache-Komprimierungsbibliotheken geliefert wird, und bin aus drei Gründen mit ihnen unzufrieden:

  1. Sie sind aufgebläht und haben ein schlechtes API-Design. Ich muss 50 Zeilen Boiler-Plate-Byte-Array-Ausgabe, Zip-Eingabe, Datei-Out-Streams und Schließen relevanter Streams schreiben und Ausnahmen abfangen und Byte-Puffer selbst verschieben . Warum kann ich keine einfache API haben, die so aussieht Zipper.unzip(InputStream zipFile, File targetDirectory, String password = null)und Zipper.zip(File targetDirectory, String password = null)einfach funktioniert?

  2. Es scheint, dass das Entpacken beim Entpacken die Metadaten der Datei zerstört und die Kennwortbehandlung fehlerhaft ist.

  3. Außerdem waren alle Bibliotheken, die ich ausprobiert habe, 2-3x langsam im Vergleich zu den Befehlszeilen-Zip-Tools, die ich mit UNIX erhalte.

Für mich sind (2) und (3) kleine Punkte, aber ich möchte wirklich eine gut getestete Bibliothek mit einer einzeiligen Schnittstelle.

Pathikrit
quelle
13
Was # 1 betrifft, liegt es daran, dass nicht jeder einfach eine Datei in ein Verzeichnis entpackt. Wenn Sie immer das gleiche Muster verwenden, warum nicht nur eine Utility - Klasse schreiben , die eine der anderen wickelt und das tut , was Sie brauchen , um es zu und benutzen Sie einfach das ?
Edward Thomson
14
@EdwardThomson, weil es einfacher ist, eine Bibliothek zu verwenden, als Code zu schreiben, Code zu testen und Code zu verwalten.
Zak
11
@ EdwardThomson: Ihr Argument ist ungültig. Schauen Sie sich die Python-Zip-API an: docs.python.org/3/library/zipfile . Sie benötigen 1 Codezeile, um Dateien zu komprimieren oder zu entpacken. APIs sollten den allgemeinen Fall sehr gut behandeln, und ich kann mir keinen Anwendungsfall einer Zip-API außer dem Zippen oder Entpacken vorstellen.
Pathikrit
7
@wrick: Das Komprimieren einer Datei oder das Entpacken einer Datei ist ein Sonderfall beim Komprimieren oder Entpacken eines Streams. Wenn Ihre API nicht zulässt, dass ich einen Stream darauf schreibe, und ich stattdessen einen Stream in eine Datei schreibe, damit ich das Ihrer API zuführen kann, ist Ihre API hirngeschädigt.
Edward Thomson
52
@EdwardThomson - Gut, also lassen Sie die Bibliothek sowohl Dateien als auch Streams unterstützen. Es ist Zeitverschwendung für alle - meine, Ihre, der Fragesteller und alle anderen Googler, die darauf stoßen werden, dass wir alle unsere eigenen Zip-Dienstprogramme implementieren müssen. So wie es TROCKEN gibt, gibt es TROPFEN - Wiederholen Sie andere Menschen nicht.
ArtOfWarfare

Antworten:

290

Ich weiß, dass es spät ist und es gibt viele Antworten, aber dieses zip4j ist eine der besten Bibliotheken zum Zippen, die ich verwendet habe. Es ist einfach (kein Boiler-Code) und kann problemlos passwortgeschützte Dateien verarbeiten.

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

Die Maven-Abhängigkeit ist:

<dependency>
    <groupId>net.lingala.zip4j</groupId>
    <artifactId>zip4j</artifactId>
    <version>1.3.2</version>
</dependency>
user2003470
quelle
1
Ich habe org.zeroturnaround.zip.ZipException: java.io.FileNotFoundException: images \ 001GL.JPG: Öffnen fehlgeschlagen: EINVAL-Fehler (ungültiges Argument)
Smit Patel
4
Funktioniert es mit Android?
Ercan
1
Nein, es funktioniert nicht gut mit Android, es unterstützt nicht die chinesische Sprache.
Dhiraj Himani
3
Zip4J unterstützt nicht das Lesen einer Zip-Datei von einem Eingabestream, sondern nur von der Festplatte.
Renaud Cerrato
2
Die Website scheint kein Javadoc zu haben.
JohnC
76

Mit Apache Commons-IO ‚s IOUtilskönnen Sie dies tun:

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

Es ist immer noch ein Boilerplate-Code, aber es gibt nur eine nicht exotische Abhängigkeit: Commons-IO

Geoffrey De Smet
quelle
1
@VitalySazanovich Sie beziehen sich auf Java 7 ZipEntry.
Randy
2
Vielen Dank. Benötigt auch zipFile.close () am Ende.
JoshuaD
4
warum nicht IOUtils.closeQuietly (out)?
Juan Mendez
2
@JuanMendez, denn wenn beim Schließen Fehler auftreten, können Sie nicht sicher sein, ob die Datei vollständig und korrekt gespeichert wurde. Aber zusätzlich zum Normalen wird close()es nicht schaden.
Vadipp
3
Diese Lösung ist anfällig für ZipSlip (zip4j ist ebenfalls betroffen )
Marcono1234
40

Extrahieren Sie die Zip-Datei und alle ihre Unterordner nur mit dem 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-Dateien und alle ihre Unterordner:

 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();
        }
    }
}
Bashir Beikzadeh
quelle
7
Die Aufrufe zum Schließen sollten sich mindestens in "finally" -Blöcken befinden. Ausnahmen werden nicht gut behandelt. -> Ich denke, das ist ein Teil des Grundes, warum das OP nach einer Bibliothek gefragt hat .
4
Das ist zu viel Code. Dies kann in 2 Zeilen erfolgen.
Makky
/mnt/sdcard/final_unzip_data/Product_images\001GL.JPG: Öffnen fehlgeschlagen: EINVAL (ungültiges Argument)
Smit Patel
@ Joe Michael Danke Kumpel für das Posten. Es löst mein Problem. Ich gebe dir +1 fürextractFolder(String zipFile,String extractFolder)
OO7
Dieser Code behält keine Dateiattribute und Berechtigungen bei. Wenn Sie zum Auspacken einer ausführbaren Anwendung so etwas verwenden, müssen Sie auf seltsame Fehler bei den Dateiberechtigungen vorbereitet sein. Das hat mich eine Woche Kopfschmerzen gekostet.
Renato
23

Eine weitere Option, die Sie auschecken können, ist zt-zip, das auf der Maven-Zentral- und Projektseite unter https://github.com/zeroturnaround/zt-zip verfügbar ist

Es verfügt über die Standardfunktion zum Packen und Entpacken (in Streams und im Dateisystem) + viele Hilfsmethoden zum Testen von Dateien in einem Archiv oder zum Hinzufügen / Entfernen von Einträgen.

toomasr
quelle
17

Vollständige Implementierung zum Zip / Entpacken eines Ordners / einer Datei mit zip4j


Laden Sie das Glas von hier herunter und fügen Sie es Ihrem Projekterstellungspfad hinzu. Der classBalg kann jede Datei oder jeden Ordner mit oder ohne Passwortschutz komprimieren und extrahieren.

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);
    }/**/
}
Minhas Kamal
quelle
1
Eine schöne Antwort und Bibliothek. Das Extrahieren von 1868-Dateien dauerte in dieser Bibliothek ~ 15 Sekunden, verglichen mit mehr als 20 Minuten bei Verwendung von ZipInputStream (aus irgendeinem Grund)
Jonty800
8

Ein sehr schönes Projekt ist TrueZip .

TrueZIP ist ein Java-basiertes Plug-In-Framework für virtuelle Dateisysteme (VFS), das transparenten Zugriff auf Archivdateien bietet, als wären sie nur einfache Verzeichnisse

Zum Beispiel (von der Website ):

File file = new TFile("archive.tar.gz/README.TXT");
OutputStream out = new TFileOutputStream(file);
try {
   // Write archive entry contents here.
   ...
} finally {
   out.close();
}
Michael
quelle
Die Bibliothek sieht gut aus - es ist immer noch nicht klar, wie man eine Zip-Datei einfach mit einem Zipinputstream / Datei / Pfad entpackt.
Pathikrit
1
TrueZIP scheint das Lesen aus Streams nicht sehr gut zu handhaben.
Teo Klestrup Röijezon
5
Ist es nicht weitgehend dasselbe wie das, was Sie in Java 7 tun können? (siehe ZipFileSystemProvider ).
Peterh
1
@peterh: Der Standard-JDK ZipFileSystemProvider wäre eine gute Antwort. Nur wenige Leute sehen es als Kommentar.
Iuzuz
3

Eine weitere Option ist JZlib . Nach meiner Erfahrung ist es weniger "dateizentriert" als zip4J. Wenn Sie also an In-Memory-Blobs anstatt an Dateien arbeiten müssen, sollten Sie es sich ansehen.

Henrik Aasted Sørensen
quelle
0

Haben Sie sich http://commons.apache.org/vfs/ angesehen ? Es behauptet, viele Dinge für Sie zu vereinfachen. Aber ich habe es nie in einem Projekt verwendet.

Mir sind auch keine anderen Java-Native-Komprimierungsbibliotheken als das JDK oder die Apache-Komprimierung bekannt.

Ich erinnere mich, dass wir einige Funktionen aus Apache Ant herausgerissen haben - sie haben viele Utils für die Komprimierung / Dekomprimierung eingebaut.

Beispielcode mit VFS würde folgendermaßen aussehen:

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
quelle
1
Es sieht so aus, als ob die Unterstützung für Zip-Dateien im VFS- Material für
TJ Crowder