Che cos'è una buona libreria Java per comprimere / decomprimere i file? [chiuso]


232

Ho esaminato la libreria Zip predefinita fornita con le librerie di compressione JDK e Apache e non sono soddisfatta per 3 motivi:

  1. Sono gonfiati e hanno un design API errato. Devo scrivere 50 righe di output array di byte della piastra della caldaia, input zip, file di flussi e chiudere flussi pertinenti e rilevare eccezioni e spostare i buffer di byte per conto mio ? Perché non posso avere una semplice API simile a questa Zipper.unzip(InputStream zipFile, File targetDirectory, String password = null)e Zipper.zip(File targetDirectory, String password = null)che funzioni?

  2. Sembra che zippare decomprimere distrugga i metadati dei file e la gestione delle password sia interrotta.

  3. Inoltre, tutte le librerie che ho provato erano 2-3 volte più lente rispetto agli strumenti zip della riga di comando che ottengo con UNIX?

Per me (2) e (3) sono punti minori ma voglio davvero una buona libreria testata con un'interfaccia a una riga.


13
Per quanto riguarda il numero 1, è perché non tutti stanno semplicemente decomprimendo un file in una directory. Se si sta utilizzando sempre lo stesso modello, perché non basta scrivere una classe di utilità che avvolge uno degli altri e fa quello che avete bisogno di e basta usare quella ?
Edward Thomson,

14
@EdwardThomson perché è più facile usare una libreria che scrivere codice, testare codice e mantenerlo.
Zak,

11
@EdwardThomson: il tuo argomento non è valido. Guarda l'API zip di Python: docs.python.org/3/library/zipfile . È necessaria 1 riga di codice per comprimere o decomprimere i file. Le API dovrebbero gestire molto bene il caso comune e non riesco a pensare a nessun caso d'uso di un'API zip oltre a zippare o decomprimere.
pathikrit,

7
@wrick: zippare un file o decomprimere un file è un caso speciale di zippare o decomprimere un flusso. Se la tua API non mi consente di scrivere un flusso su di esso e invece mi fa scrivere un flusso su un file solo per poterlo alimentare alla tua API, allora la tua API è danneggiata dal cervello.
Edward Thomson,

52
@EdwardThomson - Bene, quindi fai in modo che la libreria supporti sia i file che i flussi. È una perdita di tempo per tutti: il mio, il tuo, il richiedente e tutti gli altri googler che si imbatteranno in questo che ognuno di noi dovrà implementare le proprie Zip Utility. Proprio come c'è DRY, c'è DROP - Non ripetere altre persone.
ArtOfWarfare il

Risposte:


291

So che è tardi e ci sono molte risposte ma questo zip4j è una delle migliori librerie per zippare che ho usato. È semplice (senza codice caldaia) e può gestire facilmente file protetti da password.

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

La dipendenza Maven è:

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

1
ho org.zeroturnaround.zip.ZipException: java.io.FileNotFoundException: images \ 001GL.JPG: apertura non riuscita: errore EINVAL (argomento non valido)
Smit Patel

4
Funziona con Android?
Ercan,

1
No, non funziona bene con Android, non supporta la lingua cinese.
Dhiraj Himani,

3
Zip4J non supporta la lettura di uno zip da un inputstream, solo dal disco.
Renaud Cerrato,

2
Il sito Web non sembra avere un javadoc.
Giovanni

76

Con Apache Commons-IO 's IOUtilssi può fare questo:

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

È ancora un po 'di codice boilerplate, ma ha solo 1 dipendenza non esotica: Commons-IO


1
@VitalySazanovich ti riferisci a Java 7 ZipEntry.
Randy,

2
Grazie. Inoltre ha bisogno di zipFile.close () alla fine.
Giosuè il

4
perché non IOUtils.closeQuietly (fuori)?
Juan Mendez,

2
@JuanMendez perché se ci sono errori alla chiusura, non puoi essere sicuro che il file sia stato salvato interamente e correttamente. Ma in aggiunta al normale close()non farà male.
vadipp,

3
Questa soluzione è vulnerabile a ZipSlip (anche zip4j è interessato )
Marcono1234

40

Estrai il file zip e tutte le sue sottocartelle, usando solo 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());
    }

}

File zip e tutte le sue sottocartelle:

 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
Le chiamate per chiudere dovrebbero essere almeno all'interno dei blocchi "finalmente". Le eccezioni non sono gestite bene. -> Immagino che questo sia parte del motivo per cui l'OP ha richiesto l'uso di una libreria .

4
Questo è troppo codice. Questo può essere fatto in 2 righe.
Makky

/mnt/sdcard/final_unzip_data/Product_images\001GL.JPG: apertura non riuscita: EINVAL (argomento non valido)
Smit Patel

@Joe Michael Grazie amico per aver pubblicato questo. Risolve il mio problema. Ti darò +1 perextractFolder(String zipFile,String extractFolder)
OO7 il

Questo codice non mantiene gli attributi e le autorizzazioni dei file ... se usi qualcosa di simile per decomprimere un'applicazione eseguibile, preparati a strani errori relativi alle autorizzazioni dei file. Questo mi è costato una settimana di mal di testa.
Renato,

23

Un'altra opzione che puoi verificare è zt-zip disponibile dalla pagina centrale e del progetto di Maven su https://github.com/zeroturnaround/zt-zip

Ha le funzionalità standard di impacchettamento e spacchettamento (su stream e su filesystem) + molti metodi di supporto per testare i file in un archivio o aggiungere / rimuovere voci.


17

Implementazione completa per comprimere / decomprimere una cartella / file con zip4j


Scarica il vaso da qui e aggiungilo al percorso di creazione del progetto. Il classmuggito può comprimere ed estrarre qualsiasi file o cartella, con o senza protezione password-

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
Una bella risposta e una biblioteca. L'estrazione di 1868 file ha richiesto circa 15 secondi su questa libreria, rispetto a oltre 20 minuti quando si utilizza ZipInputStream (per qualche motivo)
Jonty800

8

Un progetto molto bello è TrueZip .

TrueZIP è un framework plug-in basato su Java per file system virtuali (VFS) che fornisce un accesso trasparente ai file di archivio come se fossero semplici directory

Ad esempio (dal sito Web ):

File file = new TFile("archive.tar.gz/README.TXT");
OutputStream out = new TFileOutputStream(file);
try {
   // Write archive entry contents here.
   ...
} finally {
   out.close();
}

La libreria sembra carina - non è ancora ovvio come decomprimere semplicemente un file zip dato un zipinputstream / file / path.
pathikrit,

1
TrueZIP non sembra gestire molto bene la lettura da stream.
Teo Klestrup Röijezon,

5
Non è sostanzialmente lo stesso di quello che puoi fare in Java 7? (guarda ZipFileSystemProvider ).
Pietro,

1
@peterh: ZipFileSystemProvider standard-JDK sarebbe una buona risposta. Solo poche persone lo vedono come commento.
iuzuz,

3

Un'altra opzione è JZlib . Nella mia esperienza, è meno "centrato sui file" di zip4J, quindi se hai bisogno di lavorare su BLOB in memoria piuttosto che sui file, potresti dare un'occhiata.



0

Hai dato un'occhiata a http://commons.apache.org/vfs/ ? Afferma di semplificare molte cose per te. Ma non l'ho mai usato in un progetto.

Inoltre, non sono a conoscenza delle librerie di compressione native di Java diverse da JDK o Apache Compression.

Ricordo che una volta abbiamo strappato alcune funzionalità da Apache Ant: hanno molti programmi di utilità per la compressione / decompressione integrati.

Il codice di esempio con VFS sarebbe simile a:

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
Sembra che il supporto per i file zip nelle cose VFS da solo sia abbastanza limitato: commons.apache.org/vfs/filesystems.html
TJ Crowder,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.