Вы находитесь на странице: 1из 8

Compresión y Decompresión de archivos ZIP con Java

Compresión y Decompresión de archivos ZIP con Java

Java nos provee la herramienta JAR (en el directorio bin/ del JRE) para crear, ver, y extraer archivos JAR. Pero, aún más útil, también podemos encontrar todo un paquete conteniendo clases que nos permiten agregar compresión ZIP y GZIP a nuestras aplicaciones.

A través del paquete java.util.zip, Sun nos ofrece las siguientes funcionalidades:

Compresión, Decompresión, y Visualización de archivos ZIP y GZIP

Compresión y Decompresión usando el algoritmo de compresión DEFLATE (usado por ZIP y GZIP)

Clases de utilidad para calcular checksums CRC-32 y Adler-32

Compresión ZIP

Para permitirnos comprimir múltiples archivos en un solo archivo ZIP, Java nos provee de dos clases:

java.util.zip.ZipOutputStream: un wrapper output stream que sabe cómo escribir en un stream destino (usualmente un archivo) aplicando compresión ZIP.

java.util.zip.ZipEntry: usado para identi car la ubicación (path) en un archivo ZIP hacia donde el archivo comprimido será escrito.

Los pasos requeridos para comprimir archivos en un archivo ZIP son los siguientes:

Crear un ZipOutputStream que referencia el destino (por ejemplo, puede referenciar a un archivo al wrapear un ZipOutputStream que referencia el destino (por ejemplo, puede referenciar a un archivo al wrapear un java.io.FileOutputStream)

Crear un java.io.InputStream (o derivado) que referencia un archivo a agregar al ZIP java.io.InputStream (o derivado) que referencia un archivo a agregar al ZIP

Crear una ZipEntry que representa el InputStream creado en el paso 2 ZipEntry que representa el InputStream creado en el paso 2

Agregar el ZipEntry al ZipOutputStream llamando a su método ZipEntry al ZipOutputStream llamando a su método

putNextEntry() Copiar datos del InputStream al ZipOutputStream (usando un bu ff er array de bytes

putNextEntry()

Copiar datos del InputStream al ZipOutputStream (usando un bu er array de

bytes para leer desde el InputStream y luego escribirlo en el ZipOutputStream)

La clave para crear una compresión de múltiples archivos radica en ZipEntry. El ZipEntry

nos permite setear un path lógico dentro del archivo donde el siguiente conjunto de datos

binarios será escrito. Luego, cuando el archivo es descomprimido, tiene que recrear la

ubicación destino y extraer la entrada allí mismo.

La siguiente porción de código ejempli ca como comprimir una java.util.List de

nombres de archivos a un archivo ZIP destino filename.

try

{

// Reference to the file we will be adding to the zipfile BufferedInputStream origin = null; // Reference to our zip file FileOutputStream dest = new FileOutputStream( filename ); // Wrap our destination zipfile with a ZipOutputStream ZipOutputStream out = new ZipOutputStream(

new BufferedOutputStream( dest ) ); // Create a byte[] buffer that we will read data

// from the source

// files into and then transfer it to the zip file byte[] data = new byte[ BUFFER_SIZE ]; // Iterate over all of the files in our list for( Iterator i=files.iterator(); i.hasNext(); )

{

// Get a BufferedInputStream that we can use to read the

// source file

String filename = ( String )i.next(); System.out.println( "Adding: " + filename ); FileInputStream fi = new FileInputStream( filename ); origin = new BufferedInputStream( fi, BUFFER_SIZE ); // Setup the entry in the zip file

ZipEntry entry = new ZipEntry( filename ); out.putNextEntry( entry ); // Read data from the source file and write it out to the zip file int count; while( ( count = origin.read(data, 0, BUFFER_SIZE ) ) != -1 )

{

out.write(data, 0, count);

}

// Close the source file

origin.close();

}

// Close the zip file out.close();

}

catch( Exception e )

{

e.printStackTrace();

}

En este código, tenemos una java.util.List contieniendo Strings con el path completo a

los archivos a agregar en el ZIP. Una ZipEntry es agregada al ZipOutputStream para cada

archivo. El archivo es leído al crear un nuevo FileInputStream, referenciándolo y

wrapeándolo con un BufferedInputStream. Los datos son luego leídos desde el archivo

fuente en bloques de tamaño BUFFER_SIZE y escritos al ZipOutputStream. Luego de que el

archivo ha sido copiado completamente al ZipOutputStream, se cierra, y el siguiente archivo

es procesado. Esto contininúa hasta que no hay más archivos por procesar. Finalmente, el

ZipOutputStream se cierra y la operación es nalizada.

Decompresión ZIP

La decompresión es justamente lo opuesto a la compresión (obviamente, pero me estoy

reriendo a las operaciones de programación!). Leemos un archivo comprimido con ZIP

utilizando java.util.ZipInputStream y navegamos a través de su conjunto de ZipEntrys.

Los pasos son los siguientes:

de su conjunto de ZipEntry s. Los pasos son los siguientes: Crear un java.util.zip.ZipInputStream que referencia

Crear un java.util.zip.ZipInputStream que referencia a la fuente java.util.zip.ZipInputStream que referencia a la fuente

comprimida (por ejemplo, para leer un archivo debemos hacer que el

ZipInputStream wrapee un java.io.FileInputStream)

Iterar sobre todas las ZipEntry s contenidas en el archivo al llamar al método ZipEntrys contenidas en el archivo al llamar al método

getNextEntry() de ZipInputStream

Crear un java.io.OutputStream que referencia el destino (por ejemplo, un java.io.OutputStream que referencia el destino (por ejemplo, un

FileOutputStream) que coincide con el path del ZipEntry

Copiar los datos desde el ZipInputStream hacia el OutputStream , hasta que el ZipInputStream hacia el OutputStream, hasta que el

stream nalize (esto señala el n del ZipEntry)

Cerrar el OutputStream OutputStream

Obtener el siguiente ZipEntry (volviendo al paso 2) ZipEntry (volviendo al paso 2)

La siguiente porción de código muestra cómo descomprimir archivos de un archivo ZIP hacia

sus ubicaciones apropiadas en el sistema de archivo. Asume que el nombre del archivo ZIP ha

sido especi cado a través de filename, y que el directorio destino ha sido especi cado con

destination.

try { // Create a ZipInputStream to read the zip file BufferedOutputStream dest = null; FileInputStream fis = new FileInputStream( filename ); ZipInputStream zis = new ZipInputStream(

new BufferedInputStream( fis ) ); // Loop over all of the entries in the zip file int count; byte data[] = new byte[ BUFFER_SIZE ]; ZipEntry entry; while( ( entry = zis.getNextEntry() ) != null )

{

if( !entry.isDirectory() )

{

String entryName = entry.getName(); prepareFileDirectories( destination, entryName );

String destFN = destination + File.separator + entry.getName(); // Write the file to the file system FileOutputStream fos = new FileOutputStream( destFN ); dest = new BufferedOutputStream( fos, BUFFER_SIZE ); while( (count = zis.read( data, 0, BUFFER_SIZE ) ) != -1 )

{

dest.write( data, 0, count );

}

dest.flush();

dest.close();

}

}

zis.close();

}

catch( Exception e )

{

e.printStackTrace();

}

Como en el ejemplo anterior, BUFFER_SIZE puede ser 8192 (8 K), pero esta conguración

puede ser cambiada a gusto. El método getName() de ZipEntry retorna el nombre

completo, incluyendo el path, hacia el archivo destino.

Un método de ayuda asegura que el directorio existe (o lo crea si no existe) antes de escribir el

archivo de salida:

prepareFileDirectories()

Visualización de ZIP

Comparado a la decompresión de archivos ZIP, la visualización de los contenidos es casi tan

trivial. Todo lo que necesitamos hacer es crear un ZipInputStream, apuntarlo a nuestro

archivo ZIP, e iterar sobre los ZipEntrys sin necesidad de extraer nada. La siguiente porción

de código lo ejemplica.

try

{

FileInputStream fis =

new FileInputStream( this.filename ); ZipInputStream zis = new ZipInputStream(

new BufferedInputStream( fis ) ); // Loop over all of the entries in the zip file ZipEntry entry; DateFormat df = DateFormat.getDateTimeInstance(

DateFormat.SHORT, DateFormat.SHORT );

while( ( entry = zis.getNextEntry() ) != null )

{

if( !entry.isDirectory() )

{

System.out.println( entry.getName() + ", " +

df.format( new Date( entry.getTime() ) ) + ", " + entry.getSize() + ", " + entry.getCompressedSize() );

}

}

}

catch( Exception e )

{

e.printStackTrace();

}

Este código asume que el archivo ZIP está representado por filename. Crea un

FileInputStream que referencia al archivo, lo wrapea con un ZipInputStream, y luego

itera sobre los contenidos usando el método getNextEntry() de ZipInputStream. Luego

muestra información del archivo utilizando un sobconjunto de la información que provee la

clase ZipEntry.

He aquí lo que podemos encontrar en la clase ZipEntry:

String getComment(): Retorna el comentario para la entrada, o null si no posee. : Retorna el comentario para la entrada, o null si no posee.

long getCompressedSize(): Retorna el tamaño de los datos comprimidos para la : Retorna el tamaño de los datos comprimidos para la

entrada, o -1 si es desconocido.

long getCrc(): Retorna el checksum CRC-32 de los datos de la entrada descomprimida, o : Retorna el checksum CRC-32 de los datos de la entrada descomprimida, o

-1 si es desconocido.

byte[] getExtra(): Retorna la información extra para esta entrada, o null si no posee. : Retorna la información extra para esta entrada, o null si no posee.

int getMethod(): Retorna el método de compresión para esta entrada, o -1 si no está : Retorna el método de compresión para esta entrada, o -1 si no está

especi cado.

String getName(): Retorna el nombre de la entrada. : Retorna el nombre de la entrada.

long getSize(): Retorna el tamaño descomprimido de los datos de entrada, o -1 si es : Retorna el tamaño descomprimido de los datos de entrada, o -1 si es

desconocido.

long getTime(): Retorna la fecha y hora de modi fi cación de la entrada, o -1 : Retorna la fecha y hora de modi cación de la entrada, o -1 si no está

especi cado.

boolean isDirectory(): Retorna true si esta es una entrada de directorio. : Retorna true si esta es una entrada de directorio.