Compresrión en Java

Compresión y descompresión de datos utilizando Java
Autor: Qusay H. Mahmoud/Konstantin Kladko
Traducción/Adaptación: RuGI
URL Original:http://developer.java.sun.com/developer/technicalArticles/Programming/compression/
Muchas fuentes de información contienen datos redundantes o datos que
no son muy revelantes para la información que se quiere guardar.
Esto produce grandes cantidades de datos que se transfieren entre el
cliente y el servidor de aplicaciones o entre computadoras en general.
La solución obvia a estos problemas de almacenamiento de los datos y
traslado de la información es instalar dispositivos del almacenamiento adicionales
y extender los medios de comunicación existentes. Hacer esto, sin embargo, requiere un
incremento en los costos de operación en una organización. Un método para reducir
una porción el almacenamiento de los datos y la tranferencia de la información es a
través de representaciones de los datos con un codigo mas eficiente. Este artículo
presenta una ligera introducción a la compresion y descompresion de datos,
y muestra cómo comprimir y descomprimir estos, eficaz y convenientemente,
desde tus aplicaciones Java usando el paquete java.util.zip
Mientras qué es posible comprimir y descomprimir datos usando herramientas comoWinZip
, gzip
, y JAR
, éstas se usan como aplicaciones aisladas. Es posible invocar estas herramientas desde tus aplicaciones
Java, pero ésta no es un manera correcta de utilizarlas y no es una
solución eficaz. Este artículo:
- Muestra brevemente las generalidades de la compresión de datos
- Describe el paquete
java.util.zip
- Muestra cómo usar este paquete para comprimir y descomprimir datos
- Muestras cómo comprimir y descomprimir objetos serializados para ahorrar espacio en disco
- Muestras cómo comprimir y descomprimir datos al vuelo (antes de transmitir) para mejorar el desempeño de aplicaciones cliente/servidor
Generalidades de la compresión de datos
Un ejemplo sencillo de redundancia de datos en un archivo es la repetición de
caracteres. Por ejemplo, considera la siguiente cadena:
BBBBHHDDXXXXKKKKWWZZZZ
Este cadena puede ser codificada y compactada reemplazando cada subcadena repetida
de carácteres por un solo carácter repetido y un número que representa el número de
veces que el carácter se repite. La cadena anterior puede codificarse así:
4B2H2D4X4K2W4Z
Aqui, "4B" significa cuatro B's, "2H" significa dos H's, y asi sucesivamente.
La compresión de cadenas con este algoritmo se conoce somo RLE (run-length
encoding)
Como otro ejemplo, considera el almacenamiento de una imagen rectangular. Como una
sencilla imagen de un mapa de bits, ésta se puede almacenar como muestra la Figura 1.
0000000000000000000000000000000000000000
0000000000000000000000000000000000000000
0000000000111111111111111111110000000000
0000000000100000000000000000010000000000
0000000000100000000000000000010000000000
0000000000100000000000000000010000000000
0000000000111111111111111111110000000000
0000000000000000000000000000000000000000
Fig.1. Un mapa de bits con información para el RLE
Otro manera podría ser guardar la imagen como un metafile gráfico:
Rectangle 11, 3, 20, 5
Que significa; el rectángulo comienza en la coordenada (11,3) y tiene 20 pixeles
de ancho y 5 de alto.
La imagen rectangular puede comprimirse utilizando RLE a través del conteo de
bits identicos, así:
0, 40
0, 40
0,10 1,20 0,10
0,10 1,1 0,18 1,1 0,10
0,10 1,1 0,18 1,1 0,10
0,10 1,1 0,18 1,1 0,10
0,10 1,20 0,10
0,40
La primera línea nos dice que la primera línea del mapa de bit's consiste en 40 0's.
La tercera línea dice que la tercera línea del mapa de bit's consiste en 10 0's
seguidos por 20 1's y seguidos por 10 0's más, y así sucesivamente para las otras
líneas.
Nota que RLE requiere representaciones diferentes para el archivo y su
versión codificada, dependiendo del tipo del archivo. Por consiguiente, este método
no puede trabajar con todos los tipos de archivos. Otras técnicas de compresion
incluyen variaciones de RLE, una es VLE variable-length encoding
(también conocida como código Huffman ), y muchas otros. Para más
información, hay muchos libros disponible sobre técnicas de compresion de datos é
imagenes.
Hay muchos beneficios con la compresión de los datos. No obtante, la ventaja
principal es reducir requisitos de almacenamiento. También, para comunicaciones de
los datos, la transferencia de datos comprimidos aumenta la cantidad de información
transmitida. Observa que la compresión de datos puede implementarse con el hardware
ya existente, con software o usando dispositivos de hardware especiales que ya
incorporán las técnicas de compresión. La Figura 2 muestra un diagrama de bloques basico
sobre la compresión de datos.
_________________ | ||
----------------------> | Compresion | ----------------------> |
Datos Originales | de | Datos comprimidos |
<--------------------- | datos | <--------------------- |
_________________ |
Fig.2. Diagrama de Bloques. Compresión de datos
ZIP vs GZIP
Si estás trabajando con Windows, podrías estar familiarizado con la herramienta
WinZip que se utiliza para crear un archivo comprimido y para extraer archivos de un
archivo comprimido. En UNIX, sin embargo, las cosas se hace de forma ligeramente
distinta. El comando tar
se usa para crear un archivo (no
comprimido) y otro programa (gzip
o compress
) se usa para
comprimir el archivo.
El paquete java.util.zip
Java proporciona el paquete java.util.zip
para trabajar con archivos
compatibles con el formato zip. este paquete proporciona clases que te permiten leer,
crear, y modificar archivos con los formatos ZIP y GZIP. También te proporciona
clases para comprobar las sumas de control de flujos de entrada y puede ser usado
para validar entradas de datos. Este paquete proporciona una interface, catorce
clases, y dos clases de excepciones como se muestra en la Tabla 1.
Tabla 1. El paquete java.util.zip
Elemento | Tipo | Descripción |
---|---|---|
Checksum | Interface | Representa un dato de la suma de control (data checksum). Implementado por las clases Adler32 y CRC32 |
Adler32 | Clase | Usado para calcular la suma de control(checksum) de un flujo de datos |
CheckedInputStream | Clase | Un flujo de entrada que guarda la suma de control de los datos que estan siendo leídos. |
CheckedOutputStream | Clase | Un flujo de salida que guarda la suma de control de los datos que estan siendo escritos. |
CRC32 | Clase | Usado para calcular la suma de control CRC32 de un flujo dedatos |
Deflater | Clase | Soporte para compresion usando la librería ZLIB |
DeflaterOutputStream | Clase | Un flujo de salida filtrado para la compresión de datos con el formato de compresión deflate |
GZIPInputStream | Clase | Un flujo de entrada filtrado para leer datos comprimidos con el formtatoGZIP |
GZIPOutputStream | Clase | Un flujo de salida filtrado para escribir datos comprimidos con el formatoGZIP |
Inflater | Clase | Soporte para descompresion usando la librería ZLIB |
InflaterInputStream | Clase | Un flujo de entrada filtrado para la descompresion de datos con el formato de compresión deflate |
ZipEntry | Clase | Representa la entrada de un archivo ZIP |
ZipFile | Clase | Usado para leer entradas de un archivo ZIP |
ZipInputStream | Clase | Un filtro de entrada para leer archivos con el formato ZIP |
ZipOutputStream | Clase | Un filtro de salida para escribir archivos con el formatoZIP |
DataFormatException | Clase de excepcion | Lanza una excepción para señalar un error en el formato de los datos |
ZipException | Clase de exepcion | Lanza una excepción para señalar un error en el formato Zip |
Nota: La librería de compresión ZLIB se desarrolló inicialmente como parte
de la norma PNG(Portable Network Graphics) que no esta protegida por
patentes.
Comprimiendo y descomprimiendo datos desde un archivo ZIP
El paquete java.util.zip
proporciona clases para la
compresión y descompresión de datos. La descompresión de un archivoZIP
es solo la lectura de datos desde un flujo de entrada.
El paquete java.util.zip
proporciona la claseZipInputStream
para leer archivos ZIP
. UnZipInputStream
se crea como cualquier otro flujo de
entrada. Por ejemplo, el siguiente fragmento de código se usa para
crear un flujo de entrada para leer datos de un archivo con formatoZIP
:
FileInputStream fis = new FileInputStream("figs.zip");
ZipInputStream zin = new
ZipInputStream(new BufferedInputStream(fis));
Una vez creado el flujo de entrada ZIP
, puedes leer las
entradas del archivo ZIP
usando el método getNextEntry
que retorna un objeto ZipEntry
. Si se alcanza el fin del archivo(EOF) getNextEntry
retornanull
:
ZipEntry entry;
while((entry = zin.getNextEntry()) != null) {
// extraer los datos
// abrir flujos de salida
}
Ahora, es momento de preparar un flujo de salida descomprimido, esto se puede hacer de la siguiente manera:
int BUFFER = 2048;
FileOutputStream fos = new
FileOutputStream(entry.getName());
BufferedOutputStream dest = new
BufferedOutputStream(fos, BUFFER);
Nota: En el código anterior se usoBufferedOutputStream
en lugar deZIPOutputStream
.ZipOutputSream
yGZIPOutputStream
usan un buffer interno de 512 bytes. El uso deBufferedOutputStream
es justificado únicamente cuando el tamaño del buffer a utilizar es mucho mas grande que 512 bytes (en este ejemplo 2048). Mientras queZipOutputSream
no te permite modificar el tamaño del buffer,GZIPOutputStream
si lo hace; puedes especificar el tamaño del buffer interno como un argumento de su constructor.
En este segmento de código, un flujo de salida de archivo se crea usando el nombre de la(s) entrada(s) ZIP
que puede recuperarse usando el método entry.getName
. La fuente de datos ZIP
es entonces leida y se escribe en un flujo descomprimido:
while ((count = zin.read(data, 0, BUFFER)) != -1) {
//System.out.write(x);
dest.write(data, 0, count);
}
y finalmente, cerramos los flujos de entrada y salida:
dest.flush();
dest.close();
zin.close();
El codigo del Ejemplo 1 muestra como descomprimir y extraer archivos desde un archivo
ZIP
. Para probar este ejemplo, compila y ejecuta el ejemplo pasándole como parámetro un archivo comprimido con el formato ZIP
:prompt> java UnZip somefile.zip
Nota que
somefile.zip
debe ser un archivo creado usandoalguna herramienta compatible con el formato
ZIP
, comoWINZIP
.Ejemplo 1: UnZip.java
import java.io.*;
import java.util.zip.*;
public class UnZip {
static final int BUFFER = 2048;
public static void main (String argv[]) {
try {
BufferedOutputStream dest = null;
FileInputStream fis = new
FileInputStream(argv[0]);
ZipInputStream zis = new
ZipInputStream(new BufferedInputStream(fis));
ZipEntry entry;
while((entry = zis.getNextEntry()) != null) {
System.out.println("Extracting: " +entry);
int count;
byte data[] = new byte[BUFFER];
// Escribir los archivos en el disco
FileOutputStream fos = new
FileOutputStream(entry.getName());
dest = new
BufferedOutputStream(fos, BUFFER);
while ((count = zis.read(data, 0, BUFFER))
!= -1) {
dest.write(data, 0, count);
}
dest.flush();
dest.close();
}
zis.close();
} catch(Exception e) {
e.printStackTrace();
}
}
}
Es importante hacer notar que la clase ZipInputStream
lee archivos ZIP
secuencialmente. La clase ZipFile
, en cambio, lee el contenido de un archivo ZIP
usando un acceso aleatorio interno para que las entradas del archivo ZIP
no tengan que ser leídas secuencialmente
Nota: Otra diferencia fundamental entreZipInputSream
yZipFile
és en terminos del uso de la memoria de intercambio(chaché).
Las entradasZIP
no son puestas en la memoria de intercambio cuando el archivo es leido usando una combinación deZipInputStream
yFileInputStream
. Por otro lado, si el archivo es abierto usandoZipFile(fileName)
entonces es colocado en la memoria intermedia, así siZipFile(fileName)
es llamado otra vez el archivo sólo se abre una vez. Si trabajas sobre UNIX, cabe mencionar que todos los archivosZIP
abiertos usandoZipFile
son abiertos utilizando memoria mapeada, y por consiguiente el desempeño deZipFile
es superior aZipInputStream
. Por otro lado, si los contenidos del mismoZIP
, son frecuentemente modificados y recargados durante la ejecución del programa entonces se recomienda el uso deZipInputStream
A continuacion se muestra como se puede descomprimir un archivo
ZIP
utilizando la clase ZipFile
:- Crear un objeto
ZipFile
especificando el archivoZIP
a ser leido, ya sea como un - Usa el método
entries
, que retorna un objetoEnumeration
, después con un ciclo obten todos los objetosZipEntry
del archivo a descomprimir:while(e.hasMoreElements()) {
entry = (ZipEntry) e.nextElement();
// lee el contenido y guardalo
} - Lee el contenido de un objeto
ZipEntry
en especifico dentro del archivoZIP
pasando el objetoZipEntry
al métodogetInputStream
, que retornará un objetoInputStream
desde el cual puedes leer el contenido de las entradas:is = new
BufferedInputStream(zipfile.getInputStream(entry)); - Recupera el nombre de la entrada y crea un flujo de salida para guardarlo:
byte data[] = new byte[BUFFER];
FileOutputStream fos = new
FileOutputStream(entry.getName());
dest = new BufferedOutputStream(fos, BUFFER);
while ((count = is.read(data, 0, BUFFER)) != -1){
dest.write(data, 0, count);
} - Finalmente, cierra todos los fujos de entrada y salida:
dest.flush();
dest.close();
is.close();
String
o como un objeto File
:
ZipFile zipfile = new ZipFile("figs.zip");
El código completo de este programa se muestra en el ejemplo 2. Nuevamente, para probar esta clase, compilalo y ejecutalo pasandole como argumento un archivo con formato
ZIP:
prompt> java UnZip2 somefile.zip
Ejemplo 2: UnZip2.java
import java.io.*;
import java.util.*;
import java.util.zip.*;
public class UnZip2 {
static final int BUFFER = 2048;
public static void main (String argv[]) {
try {
BufferedOutputStream dest = null;
BufferedInputStream is = null;
ZipEntry entry;
ZipFile zipfile = new ZipFile(argv[0]);
Enumeration e = zipfile.entries();
while(e.hasMoreElements()) {
entry = (ZipEntry) e.nextElement();
System.out.println("Extracting: " +entry);
is = new BufferedInputStream
(zipfile.getInputStream(entry));
int count;
byte data[] = new byte[BUFFER];
FileOutputStream fos = new
FileOutputStream(entry.getName());
dest = new
BufferedOutputStream(fos, BUFFER);
while ((count = is.read(data, 0, BUFFER))
!= -1) {
dest.write(data, 0, count);
}
dest.flush();
dest.close();
is.close();
}
} catch(Exception e) {
e.printStackTrace();
}
}
}
Comprimiendo y Archivando Datos en un Archivo ZIP
la clase
ZipOutputStream
se usa para compimir datos a un archivo ZIP
.ZipOutputStream
escribe datos, en un flujo de salida, en un archivo con formato ZIP
. La creación de un archivo ZIP
involucra la siguiente serie de pasos:- El primer paso es crear un objeto
ZipOutputStream
; le pasaremos como parámetro el flujo de salida del archivo al cual queremos escribir. Aqui se muestra la manera de crear un archivoZIP
llamado "myfigs.zip":
FileOutputStream dest = new
FileOutputStream("myfigs.zip");
ZipOutputStream out = new
ZipOutputStream(new BufferedOutputStream(dest)); - Una vez que el flujo de salida objetivo es creado, el siguiente paso es abrir el o los archivos origen de los datos. En este ejemplo, los archivos a partir de los cuales se creará el archivo
ZIP
son aquellos archivos que se escuentren en el directorio actual. El métodolist
se usa en este ejemplo para obtener la lista de los archivos que se encuentran en el directorio actual:
File f = new File(".");
String files[] = f.list();
for (int i=0; i<files.length; i++) {
System.out.println("Adding: "+files[i]);
FileInputStream fi = new FileInputStream(files[i]);
// creamos una entrada zip
// agregamos entradas zip al archivo
}
Nota:El código anterior es capaz de comprimir todos los archivos que se encuentran
en el directorio actual. No maneja subdirectorios. Como ejercicio, puedes
intentar modificar el ejemplo 3 para que maneje subdirectorios - Creamos un
ZipEntry
por cada archivo leido:
ZipEntry entry= new ZipEntry(files[i]) - Antes de escribir los datos en el flujo
ZIP
de salida, debes primero colocarle el objetoZipEntry
usando el métodoputNextEntry
:
out.putNextEntry(entry); - Escribimos los datos en el archivo
ZIP
:
int count;
while((count = origin.read(data, 0, BUFFER)) != -1) {
out.write(data, 0, count);
} - Finalmente, cerramos los flujos de entrada y salida:
origin.close();
out.close();
El código completo de todo lo anterior se muestra en el ejemplo 3.
Ejemplo 3: Zip.java
import java.io.*;
import java.util.zip.*;
public class Zip{
static final int BUFFER = 2048;
public static void main(String argv[]){
try{
BufferedInputSream origin = null;
FileOutputStream dest = new
FileOutputStream("c:\\temp\\myfigs.zip");
ZipOutputStream out = new ZipOutputStream( new
BufferedOutputStream(dest));
//out.setMethod(ZipOutputStream.DEFLATED);
byte data[] = new byte[BUFFER];
//obtenemos la lista de los archivos del directorio actual
File f = File(".");
String files[] = f.list();
for (int i=0; i>files.length; ++i){
System.out.println("Adding: "+file[i]);
FileInputStream fi = new
FileInputStream(files[i]);
origin = new
BufferedInputSteam(fi, BUFFER);
ZipEntry entry = new ZipEntry(files[i]);
out.putNextEntry(entry);
int count;
while((count = origin.read(data, 0,
BUFFER))!= -1){
out.write(data, 0, count);
}
origin.close();
}
out.close();
}catch(Exception e){
e.printStrackTrace();
}
}
}
Nota:Las entradas pueden ser agregadas al archivoZIP
de forma comprimida (DEFLATED) o no comprimida(STORED). El métodosetMethod
se usa para fijar el método de almacenamiento. Por ejemplo, para fijar el método a DEFLATED (comprimido) es:out.setMethod(ZipOutputStream.DEFLATED)
y para fijarlo a
STORED (no comprimido) es:out.setMethod(ZipOutputStream.DEFLATED)
Propiedades de un Archivo ZIP
La clase
ZipEntry
describe un archivo comprimido almacenadoen un archivo
ZIP
. Los distintos métodos contenidos en esta clase se usan para fijar u obtener fragmentos de información acerca de la entrada actual.la clase
ZipEntry
se usa con las clases ZipFile
y ZipInputStream
para leer archivos ZIP
. Algunos de los métodos mas utilizados disponibles en la clase ZipEntry
son los que se muestran junto con una descripcion en la tabla 2.Tabla 2. Algunos métodos utiles de la clase ZipEntry
Método | Descripcion |
---|---|
public String getComment() | Retorna un comentario sobre la entrada, null |
public long getCompressedSize() | Retorna el tamaño comprimido de la entrada, -1 si se desconoce |
public int getMethod() | Retorna el método de compresión de la entrada, -1 si no esta especificado |
public String getName() | Retorna el nombre de la entrada |
public long getSize() | Retorna el tamaño sin comprimir de la entrada, -1 si se desconoce |
public long getTime() | Retorna la fecha de modificacion de la entrada, -1 si no esta especificado |
public void setComment(String c) | Fija un comentario opcional para la entrada |
public void setMethod(int method) | Fija el método de compresion para la entrada |
public void setSize(long size) | Fija el tamaño sin comprimir de la entrada |
public void setTime(long time) | Fija la fecha de modificacion de la entrada |
Suma de Comprobación (Checksum)
Algunas otras clases importantes del paquete
java.util.zip
son Adler32
y CRC32
, estas clases implementan la interface java.util.zip.Checksum
y calculan la suma de comprobación requerida para la compresión de datos. El algoritmo Adler32
se conoce por ser mas rápido que el CRC32
, y éste ultimo es conocido por ser más confiable.
las sumas de comprobación se usan para detectar archivos o mensajes corruptos. Por ejemplo, imagina que quieres crear un archivo ZIP
y después transferirlo a una PC remota. Una vez que está en la máquina remota, usando la suma de comprobacion, puedes verificar si el archivo se corrompió durante la transmisión. Para mostrar como crear una suma de verificacion, hemos modificado el ejemplo 1 y el ejemplo 3, para usar CheckedInputStream y CheckedOutputStream, ésto se muestra en los ejemplos 4 y 5.
Ejemplo 4: Zip.java
import java.io.*;
import java.util.zip.*;
public class Zip {
static final int BUFFER = 2048;
public static void main (String argv[]) {
try {
BufferedInputStream origin = null;
FileOutputStream dest = new
FileOutputStream("c:\\temp\\myfigs.zip");
CheckedOutputStream checksum = new
CheckedOutputStream(dest, new Adler32());
ZipOutputStream out = new
ZipOutputStream(new
BufferedOutputStream(checksum));
//out.setMethod(ZipOutputStream.DEFLATED);
byte data[] = new byte[BUFFER];
// get a list of files from current directory
File f = new File(".");
String files[] = f.list();
for (int i=0; i<files.length; i++) {
System.out.println("Adding: "+files[i]);
FileInputStream fi = new
FileInputStream(files[i]);
origin = new
BufferedInputStream(fi, BUFFER);
ZipEntry entry = new ZipEntry(files[i]);
out.putNextEntry(entry);
int count;
while((count = origin.read(data, 0,
BUFFER)) != -1) {
out.write(data, 0, count);
}
origin.close();
}
out.close();
System.out.println("checksum:
"+checksum.getChecksum().getValue());
} catch(Exception e) {
e.printStackTrace();
}
}
}
Ejemplo 5: UnZip.java
import java.io.*;
import java.util.zip.*;
public class UnZip {
public static void main (String argv[]) {
try {
final int BUFFER = 2048;
BufferedOutputStream dest = null;
FileInputStream fis = new
FileInputStream("c:\\temp\\myfigs.zip");
CheckedInputStream checksum = new
CheckedInputStream(fis, new Adler32());
ZipInputStream zis = new
ZipInputStream(new
BufferedInputStream(checksum));
ZipEntry entry;
while((entry = zis.getNextEntry()) != null) {
System.out.println("Extracting: " +entry);
int count;
byte data[] = new byte[BUFFER];
// write the files to the disk
FileOutputStream fos = new
FileOutputStream(entry.getName());
dest = new BufferedOutputStream(fos,
BUFFER);
while ((count = zis.read(data, 0,
BUFFER)) != -1) {
dest.write(data, 0, count);
}
dest.flush();
dest.close();
}
zis.close();
System.out.println("Checksum:
"+checksum.getChecksum().getValue());
} catch(Exception e) {
e.printStackTrace();
}
}
}
Para probar los ejemplos 4 y 5, compila las clases y ejecuta primero la clase Zip
para crear un archivo ZIP
(se calculará el valor de la suma de comprobación y se mostrará en la pantalla ), después ejecuta la clase UnZip
para descomprimir el archivo (nuevamente el valor de la suma de comprobación se mostrará en la pantalla). Los dos valores deben ser exactamente iguales, de no ser así, el archivo esta corrupto. Las sumas de verificacion son muy útiles para validar datos. Por ejemplo, puedes crear un archivo ZIP
y enviarselo a un amigo junto con el valor de la suma de comprobación. Tu amigo descomprime el archivo y compara el valor de la suma de comprobación, si los dos valores son iguales, tu amigo sabe que el archivo es auténtico.
Comprimiendo de Objetos
Hemos visto cómo comprimir datos que se encuentran en forma de archivos y agregarlos a un archivo apropiado. ¿Pero qué sucede si los datos que deseamos comprimir no están disponible en un archivo? Asumamos por ejemplo, que estas transfiriendo objetos grandes a través de sockets. Para mejorar el desempeño de tu aplicación, puedes querer comprimir los objetos antes de enviarlos por la red y descomprimirlos cuando lleguen a su destino. Como otro ejemplo, digamos quieres guardar objetos en el disco en un formato comprimido. El formato
ZIP
que esta basado en registros, no es muy conveniente para este trabajo. El formato GZIP
es más apropiado ya que funciona como un sencillo flujo de datos.
Ahora, veamos un ejemplo de cómo comprimir objetos antes de escribirlos en el disco y cómo descomprimirlos después de leerlos del disco. El ejemplo 6 es una sencilla clase que implementa la interface Serializable
para indicarle a la JVM que deseamos serializar instancias de esta clase.
Ejemplo 6: Employee.java
import java.io.*;
public class Employee implements Serializable {
String name;
int age;
int salary;
public Employee(String name, int age, int salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
public void print() {
System.out.println("Record for: "+name);
System.out.println("Name: "+name);
System.out.println("Age: "+age);
System.out.println("Salary: "+salary);
}
}
Ahora, escribamos otra clase que cree un par de objetos de la clase Employee
. El ejemplo 7 crea dos objetos (sarah
y sam
) de la clase Employee
, despues guarda su estado en un archivo con un formato comprimido.
Ejemplo 7 SaveEmployee.java
import java.io.*;
import java.util.zip.*;
public class SaveEmployee {
public static void main(String argv[]) throws
Exception {
// creamos algunos objetos
Employee sarah = new Employee("S. Jordan", 28,
56000);
Employee sam = new Employee("S. McDonald", 29,
58000);
// Serializamos los objetos sarah y sam
FileOutputStream fos = new
FileOutputStream("db");
GZIPOutputStream gz = new GZIPOutputStream(fos);
ObjectOutputStream oos = new
ObjectOutputStream(gz);
oos.writeObject(sarah);
oos.writeObject(sam);
oos.flush();
oos.close();
fos.close();
}
}
Ahora, la clase
ReadEmployee
mostrada en el ejemplo 8, se usa para reconstruir el estado de los dos objetos. Una vez que el estado se ha reconstruido, el método print
se invoca en ellos.Ejemplo 8: ReadEmployee.java
import java.io.*;
import java.util.zip.*;
public class ReadEmployee {
public static void main(String argv[]) throws
Exception{
//des-serializamos los objetos sarah y sam
FileInputStream fis = new FileInputStream("db");
GZIPInputStream gs = new GZIPInputStream(fis);
ObjectInputStream ois = new ObjectInputStream(gs);
Employee sarah = (Employee) ois.readObject();
Employee sam = (Employee) ois.readObject();
//mostramos los datos despues de reconstruir el estado de los objetos
sarah.print();
sam.print();
ois.close();
fis.close();
}
}
La misma idea se puede usar para comprimir grandes objetos que son enviados a través de sockets. El siguiente fragmento de código muestra como escribir objetos en un formato comprimido, desde el lado del servidor para el cliente:
// escribir para el cliente
GZIPOutputStream gzipout = new
GZIPOutputStream(socket.getOutputStream());
ObjectOutputStream oos = new
ObjectOutputStream(gzipout);
oos.writeObject(obj);
gzipos.finish();
Y, el siguiente fragmento de código muestra como descomprimir estos objetos del lado del cliente una vez recibidos desde el servidor:
// leer desde el servidor
Socket socket = new Socket(remoteServerIP, PORT);
GZIPInputStream gzipin = new
GZIPInputStream(socket.getInputStream());
ObjectInputStream ois = new ObjectInputStream(gzipin);
Object o = ois.readObject();
¿Y los archivos JAR?
El formato JAR(Java ARchive) esta basado en el formato normal
ZIP
con un archivo manifiesto opcional. Si deseas crear archivos JAR o extraer archivos desde una archivo JAR desde tus aplicaciones Java, usa el paquete java.util.jar
, que proporciona clases para leer y escribir archivos JAR. El uso de las clases proporcionadas por el paquete java.util.jar
es bastante similar al uso de las clases del paquete java.util.zip
descritas en este artículo. Por consiguiente, podras adaptar mucho del código de este artículo si deseas usar el paquete java.util.jar
.Conclusión
Este artículo planteó las
APIs
que puedes usar para comprimir y descomprimir datos desde tus aplicaciones, con ejemplos a lo largo del artículo se mostró cómo usar el paquete java.util.zip
para comprimir y descomprimir datos. Ahora tienes las herramientas necesarias para comprimir y descomprimir datos desde tus aplicaciones Java.
El artículo también muestra cómo comprimir y descomprimir datos al vuelo para reducir el tráfico de la red y mejorar el desempeño de tus aplicaciones cliente/servidor. La compresion de datos al vuelo, sin embargo, sólo mejora el desempeño de aplicaciones cliente/servidor cuando los objetos que están siendo comprimidos tiene un par de cientos de bytes como tamaño. No podras observar ésta mejoría si los objetos que se están comprimiendo y transfiriendo son sencillos objetos String
, por ejemplo.
Para más información
- href="http://java.sun.com/j2se/1.4/docs/api/java/util/zip/package-summary.html">El
paquete java.util.zip - href="http://java.sun.com/j2se/1.4/docs/api/java/util/jar/package-summary.html">El
paquete java.util.jar - href="http://java.sun.com/j2se/1.3/docs/guide/serialization">Serialización de
Objetos - href="http://developer.java.sun.com/developer/technicalArticles/ALT/sockets">
Transportanto objetos sobre Sockets
Descarga el código utilizado en este artículo.
|

Reader Comments