¿Qué es una buena biblioteca de Java para comprimir / descomprimir archivos? [cerrado]

232

Miré la biblioteca Zip predeterminada que viene con las bibliotecas de compresión JDK y Apache y no estoy contento con ellas por 3 razones:

  1. Están hinchados y tienen un mal diseño de API. ¿Tengo que escribir 50 líneas de salida de matriz de bytes de placa de caldera, entrada zip, archivar secuencias y cerrar secuencias relevantes y capturar excepciones y mover buffers de bytes por mi cuenta ? ¿Por qué no puedo tener una API simple que se vea así Zipper.unzip(InputStream zipFile, File targetDirectory, String password = null)y Zipper.zip(File targetDirectory, String password = null)que simplemente funcione?

  2. Parece que descomprimir descomprime destruye los metadatos del archivo y el manejo de contraseña está roto.

  3. Además, ¿todas las bibliotecas que probé eran 2-3 veces más lentas en comparación con las herramientas zip de línea de comandos que obtengo con UNIX?

Para mí (2) y (3) son puntos menores, pero realmente quiero una buena biblioteca probada con una interfaz de una línea.

pathikrit
fuente
13
En cuanto al número 1, es porque no todos simplemente descomprimen un archivo en un directorio. Si siempre usa el mismo patrón, ¿por qué no simplemente escribe una clase de utilidad que envuelve una de las otras y hace lo que necesita y simplemente usa eso ?
Edward Thomson
14
@EdwardThomson porque es más fácil usar una biblioteca que escribir código, probar código y mantener el código.
Zak
11
@EdwardThomson: su argumento no es válido. Mire la API zip de Python: docs.python.org/3/library/zipfile . Necesita 1 línea de código para comprimir o descomprimir archivos. Las API deberían manejar muy bien el caso común y no puedo pensar en ningún caso de uso de una API zip además de comprimir o descomprimir.
pathikrit
77
@wrick: comprimir un archivo o descomprimir un archivo es un caso especial de comprimir o descomprimir una secuencia. Si su API no me permite escribir una secuencia en él y, en cambio, me hace escribir una secuencia en un archivo solo para poder alimentarlo a su API, entonces su API está dañada en el cerebro.
Edward Thomson el
52
@EdwardThomson: está bien, así que haga que la biblioteca admita archivos y secuencias. Es una pérdida de tiempo para todos: el mío, el tuyo, el que pregunta y todos los demás Googlers que se toparán con esto de que cada uno de nosotros debe implementar nuestras propias Utilidades Zip. Así como hay DRY, hay DROP: no repitas a otras personas.
ArtOfWarfare

Respuestas:

291

Sé que es tarde y hay muchas respuestas, pero este zip4j es una de las mejores bibliotecas para comprimir que he usado. Es simple (sin código de caldera) y puede manejar fácilmente archivos protegidos con contraseña.

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 dependencia de Maven es:

<dependency>
    <groupId>net.lingala.zip4j</groupId>
    <artifactId>zip4j</artifactId>
    <version>1.3.2</version>
</dependency>
usuario2003470
fuente
1
obtuve org.zeroturnaround.zip.ZipException: java.io.FileNotFoundException: images \ 001GL.JPG: error de apertura: error EINVAL (argumento no válido)
Smit Patel
44
¿Funciona con Android?
Ercan
1
No, no funciona bien con Android, no es compatible con el idioma chino.
Dhiraj Himani
3
Zip4J no admite la lectura de un archivo zip desde un flujo de entrada, solo desde el disco.
Renaud Cerrato
2
El sitio web no parece tener un javadoc.
JohnC
76

Con Apache Commons-IO 's IOUtilsse puede hacer esto:

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

Todavía es un código repetitivo, pero solo tiene 1 dependencia no exótica: Commons-IO

Geoffrey De Smet
fuente
1
@VitalySazanovich te refieres a Java 7 ZipEntry.
Randy
2
Gracias. También necesita zipFile.close () al final.
JoshuaD
44
¿Por qué no IOUtils.closeQuietly (fuera)?
Juan Mendez
2
@JuanMendez porque si hay errores al cerrar, no puede estar seguro de que el archivo se guardó completa y correctamente. Pero además de lo normal close(), no dolerá.
vadipp
3
Esta solución es vulnerable a ZipSlip (zip4j también se ve afectado )
Marcono1234
40

Extraiga el archivo zip y todas sus subcarpetas, utilizando solo el 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());
    }

}

Archivos Zip y todas sus subcarpetas:

 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
fuente
77
Las llamadas para cerrar deben estar dentro de los bloques "finalmente" como mínimo. Las excepciones no se manejan bien. -> Supongo que eso es parte de por qué el OP solicitó una biblioteca para usar.
44
Esto es demasiado código. Esto se puede hacer en 2 líneas.
Makky
/mnt/sdcard/final_unzip_data/Product_images\001GL.JPG: error al abrir: EINVAL (argumento no válido)
Smit Patel
@ Joe Michael Gracias amigo por publicar esto. Resuelve mi problema. Te daré +1 porextractFolder(String zipFile,String extractFolder)
OO7
Este código no mantiene los atributos y permisos del archivo ... si usa algo como esto para descomprimir una aplicación ejecutable, esté preparado para errores extraños con respecto a los permisos de archivo. Esto me ha costado una semana de dolor de cabeza.
Renato
23

Otra opción que puede consultar es zt-zip disponible en la página central del proyecto y Maven en https://github.com/zeroturnaround/zt-zip

Tiene la funcionalidad estándar de empaquetado y desempaquetado (en secuencias y en el sistema de archivos) + muchos métodos auxiliares para probar archivos en un archivo o agregar / eliminar entradas.

toomasr
fuente
17

Implementación completa para comprimir / descomprimir una carpeta / archivo con zip4j


Descargue el jar desde aquí y agréguelo a la ruta de compilación de su proyecto. El siguiente classarchivo puede comprimir y extraer cualquier archivo o carpeta, con o sin protección por contraseña.

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
fuente
1
Una buena respuesta y biblioteca. La extracción de 1868 archivos tardó ~ 15 segundos en esta biblioteca, en comparación con más de 20 minutos cuando se usa ZipInputStream (por alguna razón)
Jonty800
8

Un proyecto muy bonito es TrueZip .

TrueZIP es un marco de plug-in basado en Java para sistemas de archivos virtuales (VFS) que proporciona acceso transparente a los archivos como si fueran directorios simples

Por ejemplo (del sitio web ):

File file = new TFile("archive.tar.gz/README.TXT");
OutputStream out = new TFileOutputStream(file);
try {
   // Write archive entry contents here.
   ...
} finally {
   out.close();
}
Miguel
fuente
La biblioteca se ve bien, aún no es obvio cómo simplemente descomprimir un archivo zip dado un zipinputstream / file / path.
pathikrit
1
TrueZIP no parece manejar muy bien la lectura de transmisiones.
Teo Klestrup Röijezon
55
¿No es lo mismo que lo que puedes hacer en Java 7? (mira ZipFileSystemProvider ).
Peter
1
@peterh: El estándar JDK ZipFileSystemProvider sería una buena respuesta. Solo pocas personas lo ven como comentario.
iuzuz
3

Otra opción es JZlib . En mi experiencia, está menos "centrado en el archivo" que zip4J, por lo que si necesita trabajar en blobs en memoria en lugar de archivos, puede echarle un vistazo.

Henrik Aasted Sørensen
fuente
0

¿Le echó un vistazo a http://commons.apache.org/vfs/ ? Afirma simplificar muchas cosas para usted. Pero nunca lo he usado en un proyecto.

Tampoco conozco las bibliotecas de compresión Java-Native que no sean JDK o Apache Compression.

Recuerdo que una vez que arrancamos algunas características de Apache Ant, tienen muchas utilidades para la compresión / descompresión incorporadas.

El código de muestra con VFS se vería así:

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
fuente
1
Parece que el soporte para archivos zip en el material VFS por sí solo es bastante limitado: commons.apache.org/vfs/filesystems.html
TJ Crowder