Qu'est-ce qu'une bonne bibliothèque Java pour compresser / décompresser des fichiers? [fermé]


232

J'ai regardé la bibliothèque Zip par défaut fournie avec le JDK et les bibliothèques de compression Apache et je ne suis pas satisfait d'eux pour 3 raisons:

  1. Ils sont gonflés et ont une mauvaise conception de l'API. Je dois écrire 50 lignes de sortie de tableau d'octets de plaque de chaudière, entrée zip, fichiers de sortie et fermer les flux pertinents et intercepter les exceptions et déplacer les tampons d'octets par moi-même ? Pourquoi ne puis-je pas avoir une API simple qui ressemble à ceci Zipper.unzip(InputStream zipFile, File targetDirectory, String password = null)et Zipper.zip(File targetDirectory, String password = null)qui fonctionne?

  2. Il semble que la décompression de la décompression détruit les métadonnées du fichier et que la gestion des mots de passe est rompue.

  3. De plus, toutes les bibliothèques que j'ai essayées étaient 2-3x plus lentes que les outils zip de ligne de commande que j'obtiens avec UNIX?

Pour moi (2) et (3) sont des points mineurs mais je veux vraiment une bonne bibliothèque testée avec une interface à une ligne.


13
Quant à # 1, c'est parce que tout le monde ne décompresse pas simplement un fichier dans un répertoire. Si vous utilisez toujours le même modèle, pourquoi ne pas simplement écrire une classe utilitaire qui enveloppe l' une des autres et fait ce que vous en avez besoin et juste utiliser que ?
Edward Thomson

14
@EdwardThomson, car il est plus facile d'utiliser une bibliothèque que d'écrire du code, de tester le code et de maintenir le code.
Zak

11
@EdwardThomson: Votre argument n'est pas valide. Regardez l'API zip Python: docs.python.org/3/library/zipfile . Vous avez besoin d'une ligne de code pour compresser ou décompresser des fichiers. Les API devraient très bien gérer le cas commun et je ne peux penser à aucun cas d'utilisation d'une API zip en dehors de la fermeture ou de la décompression.
pathikrit

7
@wrick: zipper un fichier ou décompresser un fichier est un cas particulier de zipper ou décompresser un flux. Si votre API ne me permet pas d'y écrire un flux et me fait plutôt écrire un flux dans un fichier juste pour que je puisse le transmettre à votre API, alors votre API est endommagée au cerveau.
Edward Thomson

52
@EdwardThomson - Très bien, alors faites en sorte que la bibliothèque prenne en charge les fichiers et les flux. C'est une perte de temps pour tout le monde - le mien, le vôtre, le demandeur et tous les autres Googlers qui tomberont sur cela que nous devons chacun mettre en œuvre nos propres utilitaires Zip. Tout comme il y a SEC, il y a DROP - Don't Repeat Other People.
ArtOfWarfare

Réponses:


291

Je sais que c'est tard et il y a beaucoup de réponses, mais ce zip4j est l'une des meilleures bibliothèques de zip que j'ai utilisées. Son simple (pas de code de chaudière) et peut facilement gérer les fichiers protégés par mot de passe.

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 dépendance Maven est:

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

1
j'ai eu org.zeroturnaround.zip.ZipException: java.io.FileNotFoundException: images \ 001GL.JPG: ouverture a échoué: erreur EINVAL (argument non valide)
Smit Patel

4
Cela fonctionne-t-il avec Android?
Ercan

1
Non, cela ne fonctionne pas bien avec Android, ce n'est pas compatible avec la langue chinoise.
Dhiraj Himani du

3
Zip4J ne prend pas en charge la lecture d'un zip à partir d'un flux d'entrée, uniquement à partir du disque.
Renaud Cerrato

2
Le site Web ne semble pas avoir de javadoc.
JohnC

76

Avec Apache Commons-IO , IOUtilsvous pouvez faire ceci:

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

C'est encore du code standard, mais il n'a qu'une seule dépendance non exotique: Commons-IO


1
@VitalySazanovich vous faites référence à Java 7 ZipEntry.
Randy

2
Merci. Nécessite également zipFile.close () à la fin.
JoshuaD

4
pourquoi pas IOUtils.closeQuietly (out)?
Juan Mendez

2
@JuanMendez car s'il y a des erreurs à la fermeture, vous ne pouvez pas être sûr que le fichier a été enregistré entièrement et correctement. Mais en plus de la normale, close()cela ne fera pas de mal.
vadipp

3
Cette solution est vulnérable à ZipSlip (zip4j est également affecté )
Marcono1234

40

Extraire le fichier zip et tous ses sous-dossiers, en utilisant uniquement le 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());
    }

}

Fichiers Zip et tous ses sous-dossiers:

 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
Les appels à la fermeture doivent être à tout le moins à l'intérieur des blocs "finalement". Les exceptions ne sont pas bien gérées. -> Je suppose que cela fait partie des raisons pour lesquelles l'OP a demandé une bibliothèque à utiliser.

4
C'est trop de code. Cela peut se faire en 2 lignes.
Makky

/mnt/sdcard/final_unzip_data/Product_images\001GL.JPG: échec d'ouverture: EINVAL (argument non valide)
Smit Patel

@Joe Michael Merci mon pote d'avoir posté ça. Cela résout mon problème. Je donnerai u +1 pourextractFolder(String zipFile,String extractFolder)
OO7

Ce code ne conserve pas les attributs et les autorisations des fichiers ... si vous utilisez quelque chose comme ça pour décompresser une application exécutable, préparez-vous à des erreurs étranges concernant les autorisations des fichiers. Cela m'a coûté une semaine de maux de tête.
Renato

23

Une autre option que vous pouvez consulter est zt-zip disponible à partir de la page centrale et du projet Maven à https://github.com/zeroturnaround/zt-zip

Il a la fonctionnalité standard de compression et de décompression (sur les flux et sur le système de fichiers) + de nombreuses méthodes d'assistance pour tester les fichiers dans une archive ou ajouter / supprimer des entrées.


17

Mise en œuvre complète pour compresser / décompresser un dossier / fichier avec zip4j


Téléchargez le pot à partir d' ici et ajoutez-le à votre chemin de génération de projet. Le classsoufflet peut compresser et extraire n'importe quel fichier ou dossier, avec ou sans protection par mot de passe-

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
Une belle réponse et une bibliothèque. L'extraction de 1868 fichiers a pris ~ 15 secondes sur cette bibliothèque, contre plus de 20 minutes lors de l'utilisation de ZipInputStream (pour une raison quelconque)
Jonty800

8

Un très beau projet est TrueZip .

TrueZIP est un framework de plug-in basé sur Java pour les systèmes de fichiers virtuels (VFS) qui fournit un accès transparent aux fichiers d'archive comme s'il s'agissait de répertoires simples

Par exemple (sur le site 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 bibliothèque a l'air bien - il n'est toujours pas évident de décompresser simplement un fichier zip en fonction d'un zipinputstream / fichier / chemin.
pathikrit

1
TrueZIP ne semble pas très bien gérer la lecture des flux.
Teo Klestrup Röijezon

5
N'est-ce pas en grande partie la même chose que ce que vous pouvez faire en Java 7? (regardez ZipFileSystemProvider ).
peterh

1
@peterh: Le ZipFileSystemProvider standard-JDK serait une bonne réponse. Seules quelques personnes y voient un commentaire.
iuzuz

3

Une autre option est JZlib . D'après mon expérience, il est moins "centré sur les fichiers" que zip4J, donc si vous devez travailler sur des objets blob en mémoire plutôt que sur des fichiers, vous voudrez peut-être y jeter un œil.



0

Avez-vous consulté http://commons.apache.org/vfs/ ? Il prétend simplifier beaucoup de choses pour vous. Mais je ne l'ai jamais utilisé dans un projet.

Je ne connais pas non plus les bibliothèques de compression Java-Native autres que JDK ou Apache Compression.

Je me souviens une fois que nous avons extrait certaines fonctionnalités d'Apache Ant - elles contiennent de nombreux utilitaires de compression / décompression.

Un exemple de code avec VFS ressemblerait à ceci:

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
Il semble que le support des fichiers zip dans les fichiers VFS soit assez limité: commons.apache.org/vfs/filesystems.html
TJ Crowder
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.