Crear un archivo Zip con Java, comprimir y descomprimir datos

Escrito por picodotdev el .
java planeta-codigo programacion
Enlace permanente Comentarios

Desde las primeras versiones de Java en el JDK se incluyen clases en el paquete java.util.zip que heredan de las del paquete java.io del sistema de entrada y salida con las que crear archivos comprimidos en formato Zip y comprimir datos en este formato. Comprimir los archivos que permite reducir sensiblemente el tamaño de algunos tipos de archivos como los basados en texto o cietos archivos que de por si no están comprimidos. En otros tipos de archivo que en su formato ya están comprimidos como las imágenes JPEG o WebP la reducción de tamaño no es tan grande. Al contrario que los los algoritmos de compresión para imágenes JPEG y WebP que son com pérdida de datos, los algoritmos de compresión de archivos son sin pérdida, esto quiere decir que los datos obtenidos al comprimirlos y posteriormente descomprimirlos son exactamente los mismos que los originales.

Los archivos de texto o binarios no comprimidos comprimirlos supone un considerable reducción respecto del tamaño del archivo original no comprimido, un archivo XML del 20 MiB se puede quedar en 1.5 MiB, es decir, una tasa de compresión de 13 o lo que es lo mismo el fichero comprimido ocupa 13 veces menos.

Aparte del ahorro de espacio, crear un archivo Zip que agrupe varios es útil en ciertos casos. Las aplicaciones web como respuesta solo pueden devolver un archivo o documento por petición, un documento HTML, un recurso o un archivo. Para devolver varios archivos a la vez como una exportación de varios documentos dada la limitación del protocolo HTTP de petición y respuesta no es posible. Para dar solución a esta limitación normalmente se crea un archivo Zip que contenga los varios a devolver. Configurar los servidores web Nginx y Apache para comprimir los recursos minimiza los datos transferidos desde el servidor al cliente.

Clases de Java para comprimir y descomprimir

Las clases del paquete java.util.zip permiten comprimir y descomprimir datos utilizando diferentes algoritmos. Se proporcionan soporte para el formato archivo Zip, GZIP, el algoritmo de compresión DEFLATE usando la librería ZLIB y otras clases para la corrección de errores o checksums.

Para la creación de archivos Zip las clases básicas son ZipEntry que representa un flujo de datos comprimido o archivo en un archivo Zip, ZipFile representa un archivo Zip, ZipInputStream que permite descomprimir los flujos de datos al leerlos y ZipOutputStream que permite comprimir flujos de datos.

Cómo crear un archivo Zip con Java

El siguiente código permite comprime varios archivos en diferentes formatos en un único archivo Zip. Este caso es el de una aplicación Java de línea de comandos, el código para una aplicación web consistiría en crear el archivo Zip y devolver como resultado el stream de ZipOutputStream de datos en el OutputStream empleado por la aplicación web para devolver el resultado al cliente.

Los archivos de extensión jar utilizados en Java para crear librerías y war para empaquetar aplicaciones web no son más que archivos Zip con estas extensiones.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package io.github.picodotdev.blogbitix.javazip;

...

public class Main {

    ...

    public void compress() throws Exception {
        ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(new File("target/file.zip"))));
        try (zos) {
            for (String f : FILES) {
                System.out.printf("Deflating %s...%n", f);
                InputStream resource = getClass().getClassLoader().getResourceAsStream("cantrbry/" + f);

                zos.putNextEntry(new ZipEntry(f));
                zos.write(resource.readAllBytes());
                zos.closeEntry();
            }
        }
    }

    ...
}
Main-compress.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Deflating alice29.txt...
Deflating asyoulik.txt...
Deflating cp.html...
Deflating fields.c...
Deflating grammar.lsp...
Deflating kennedy.xls...
Deflating lcet10.txt...
Deflating plrabn12.txt...
Deflating ptt5...
Deflating sum...
Deflating xargs.1...
System.out-compress

Cómo descomprimir un archivo Zip con Java

El proceso contrario a la compresión es la descompresión, la descompresión permite recuperar los datos originales. Para esto se utiliza la clase ZipFile en el caso de leer de un archivo, la clase ZipInputStream también permite leer el Zip creado con ZipOutputStream.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package io.github.picodotdev.blogbitix.javazip;

...

public class Main {

    private static final List<String> FILES = List.of("alice29.txt", "asyoulik.txt", "cp.html", "fields.c", "grammar.lsp", "kennedy.xls", "lcet10.txt", "plrabn12.txt", "ptt5", "sum", "xargs.1");

    ...

    public void decompress() throws Exception {
        ZipFile zf = new ZipFile(new File("target/file.zip"));
        try (zf) {
            Enumeration<? extends ZipEntry> entries = zf.entries();
            for (ZipEntry ze : Collections.list(entries)) {
                System.out.printf("Inflating %s (compressed: %s, size: %s, ratio: %.2f)%n", ze.getName(), ze.getCompressedSize(), ze.getSize(), (double) ze.getSize() / ze.getCompressedSize());
                InputStream is = zf.getInputStream(ze);
                FileOutputStream fos = new FileOutputStream(new File("target", ze.getName()));
                try (is; fos) {
                    fos.write(is.readAllBytes());
                }
            }
        }
    }

    ...
}
Main-decompress.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
Inflating alice29.txt (compressed: 54398, size: 152089, ratio: 2,80)
Inflating asyoulik.txt (compressed: 48891, size: 125179, ratio: 2,56)
Inflating cp.html (compressed: 7955, size: 24603, ratio: 3,09)
Inflating fields.c (compressed: 3116, size: 11150, ratio: 3,58)
Inflating grammar.lsp (compressed: 1216, size: 3721, ratio: 3,06)
Inflating kennedy.xls (compressed: 203986, size: 1029744, ratio: 5,05)
Inflating lcet10.txt (compressed: 144898, size: 426754, ratio: 2,95)
Inflating plrabn12.txt (compressed: 195255, size: 481861, ratio: 2,47)
Inflating ptt5 (compressed: 56459, size: 513216, ratio: 9,09)
Inflating sum (compressed: 12984, size: 38240, ratio: 2,95)
Inflating xargs.1 (compressed: 1730, size: 4227, ratio: 2,44)
System.out-decompress

Listar el contenido de un archivo Zip con Java

En Java el contenido de un ZipFile se representa con la clase ZipEntry, el siguiente código lista el contenido de un archivo Zip.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
package io.github.picodotdev.blogbitix.javazip;

...

public class Main {

    ...

    public void list() throws Exception {
        ZipFile zf = new ZipFile(new File("target/file.zip"));
        try (zf) {
            Enumeration<? extends ZipEntry> entries = zf.entries();
            for (ZipEntry ze : Collections.list(entries)) {
                System.out.printf("File %s (compressed: %s, size: %s, ratio: %.2f)%n", ze.getName(), ze.getCompressedSize(), ze.getSize(), (double) ze.getSize() / ze.getCompressedSize());
            }
        }
    }

    ...
}
Main-list.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
File alice29.txt (compressed: 54398, size: 152089, ratio: 2,80)
File asyoulik.txt (compressed: 48891, size: 125179, ratio: 2,56)
File cp.html (compressed: 7955, size: 24603, ratio: 3,09)
File fields.c (compressed: 3116, size: 11150, ratio: 3,58)
File grammar.lsp (compressed: 1216, size: 3721, ratio: 3,06)
File kennedy.xls (compressed: 203986, size: 1029744, ratio: 5,05)
File lcet10.txt (compressed: 144898, size: 426754, ratio: 2,95)
File plrabn12.txt (compressed: 195255, size: 481861, ratio: 2,47)
File ptt5 (compressed: 56459, size: 513216, ratio: 9,09)
File sum (compressed: 12984, size: 38240, ratio: 2,95)
File xargs.1 (compressed: 1730, size: 4227, ratio: 2,44)
System.out-list

Usando la aplicación integrada en el entorno de escritorio o sistema operativo para visualizar el contenido del archivo Zip se observa que contiene comprimidos el conjunto de archivos añadidos en él.

Contenido de archivo Zip

O listar el contenido de un archivo Zip desde la linea de comandos.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$ unzip -l app/target/file.zip
Archive:  file.zip
  Length      Date    Time    Name
---------  ---------- -----   ----
   152089  2020-11-15 01:55   alice29.txt
   125179  2020-11-15 01:55   asyoulik.txt
    24603  2020-11-15 01:55   cp.html
    11150  2020-11-15 01:55   fields.c
     3721  2020-11-15 01:55   grammar.lsp
  1029744  2020-11-15 01:55   kennedy.xls
   426754  2020-11-15 01:55   lcet10.txt
   481861  2020-11-15 01:55   plrabn12.txt
   513216  2020-11-15 01:55   ptt5
    38240  2020-11-15 01:55   sum
     4227  2020-11-15 01:55   xargs.1
---------                     -------
  2810784                     11 files
unzip.sh

Otros formatos de archivos comprimidos

Los algoritmos ofrecidos en la API de Java no son los únicos, hay otros con diferentes características. Zip es un formato de archivos comprimidos popular disponible en los sistemas operativos mayoritarios como Windows, GNU/Linux y macOS ofrecen soporte para comprimir y descomprimir archivos con este formato. Otro algoritmos distintos al Zip son mejores en varios aspectos, ofreciendo ratios de compresión mayores o con velocidades de compresión y descompresión mayores.

Algunos algoritmos de compresión alternativos son bzip2, lzma, lzo o zstd para los que Java no proporciona clases en el JDK y en algunos de ellos no hay ninguna librería que permite trabajar en Java con estos formatos de archivo. La única alternativa es ejecutar un proceso del sistema que invoque el comando del sistema para realizar la compresión, la descompresión o listar el contenido del archivo.

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
./gradlew run

Comparte el artículo: