Compresión ZIP en Java
Publicado en Java el 20 de October, 2007 por Lek.Había pensado titular al post como “Cuando 2 gigas no son suficiente“, pero al final lo dejaré como está. Este post va de compresión en ZIP, pero también de lo que mentaba el otro día de trampear los límites de la programación…
Un caso típico en programación es tener que comprimir un archivo o una serie de informaciones para enviarlas por HTTP, FTP, LOQUESEATP… pero, ¿qué pasa cuando la información tiene un tamaño descomunal? La clase ZipFile tiene un límite en el tamaño de ficheros que puede aceptar de 2GB. Esta limitación es externa y debida a la estructura de 32-bits, así que debería tener muy mala solución ya que no nos vale con ejecutar el programa con chopocientos gigas de RAM.
¿O no?
Rebuscando, rebuscando, por la red uno llega a casa de la abuelita y decide probar la clase ZipInputStream. Prueba de fuego, traga con el fichero (2’3 gigas sin inmutarse). Pero ahora, ¿cómo la usamos? En los códigos que yo tengo, la clase ZipFile tiene un método entries() que nos devuelve una enumeración con todas las entradas que contiene. Luego las recorremos una a una, cogemos el inputStream de cada una, leemos, escribimos y se acabó (es el ejemplo típico de compresión en ZIP):
ZipFile zip = null;
try {
zip = new ZipFile (this.origen);
} catch (ZipException e0) {
e0.printStackTrace ();
} catch (IOException e1) {
e1.printStackTrace ();
}
if (zip != null) {
Enumeration enums = zip.entries () ;
while (enums.hasMoreElements ()) {
ZipEntry entry = (ZipEntry) enums.nextElement ();
BufferedInputStream bis = null;
int tamano = 0;
final int N = 1024;
byte [] buffer = new byte [N];
int ln = 0 ;
try {
bis = new BufferedInputStream (zip.getInputStream (entry));
try {
tamano = (int) entry.getSize ();
//Escribimos el fichero
while (tamano > 0 &&
(ln = bis.read (buffer, 0, Math.min (N, tamano)))
!= -1)
{
System.out.write (buffer, 0, ln);
tamano -= ln;
}
//.... vamos cerrando cosas..
Esto ya no sirve. La nueva clase tiene un método getNextEntry() que nos devuelve las entradas una a una. Es el cambio más visible. También es importante que ya no podemos coger el inputStream de cada entrada, pues ya estamos trabajando con uno. El tercer cambio importante es que las entradas devueltas por el ZipInputStream tienen siempre tamaño -1, por lo que no podemos fiarnos de esta variable para la lectura del InputStream. Por resumir, a mí la cosa me queda en algo similar a esto:
ZipEntry entry = null;
BufferedInputStream bis = null;
while ((entry = zip.getNextEntry ()) != null) {
if (!entry.isDirectory ()) {
//Esto es siempre -1
//tamano = (int) entry.getSize ();
final int N = 1024;
byte [] buffer = new byte [N];
int ln = 0;
try {
bis = new BufferedInputStream (zip);
try {
while ((ln = bis.read (buffer,0,N)) != -1) {
System.out.write (buffer, 0, ln);
}
//.... vamos cerrando cosas..
Es un código a buen seguro francamente mejorable (si veis el real mucho más), pero lo importante es que funciona. Y parece más simple que el original. :)
Hola amigo, soy nuevo en JAVA, y tengo que hacer un proyecto q es un compresor de archivos al mejor estilo WinZip, quiero saber como hacer para comprimir/descomprimir archivos con las librerias de java, te agradezco la respuesta!.
# victor 30 de October, 2008
El ejemplo de compresión lo tienes en este mismo post ;)
# Lek 1 de November, 2008