Manejar ficheros zip con java

De ChuWiki

Crear un fichero zip[editar]

Vamos a ver un ejemplo sencillo de cómo crear un fichero zip usando java. Para el ejemplo, tendremos un fichero.txt con un trozo de texto y lo vamos a empaquetar en un fichero.zip. Usaremos las clases que nos proporciona java en su paquete java.util.zip

En primer lugar creamos un OutputStream para nuestro fichero zip, de forma que podamos ir poniendo en él nuestros ficheros comprimidos. Este OutputStream se obtiene instanciando un ZipOutputStream

ZipOutputStream os = new ZipOutputStream(new FileOutputStream("fichero.zip"));

Podemos indicar el nivel y tipo de compresión que queremos con los métodos setMethod() y setLevel() de la clase ZipOutputStream. Los valores que admiten estos parámetros. Los dejaremos en sus valores por defecto que son DEFLATED para setMethod() (es el algoritmo por defecto) y setLevel() con DEFAULT_COMPRESSION, ambas constantes definidas en la clase Deflater

// Estas son las opciones por defecto, no es
// necesario ponerlas en codigo.
os.setLevel(Deflater.DEFAULT_COMPRESSION);
os.setMethod(Deflater.DEFLATED);

Una vez hecho esto, sólo debemos ir añadiendo ficheros. Para ello, los pasos que hay que dar son

  • Indicarle a ZipOutputStream que comenzamos una nueva entrada (un nuevo fichero)
  • Pasarle los bytes de esa nueva entrada (del fichero). Se pasan sin comprimir y ya se encargará de ir comprimiéndolos según los metemos.
  • Indicarla a ZipOutputStream que terminamos la nueva entrada.
  • Repetir los pasos anteriores mientras queramos seguir metiendo ficheros

Para indicarle a ZipOutputStream que comenzamos a meter un nuevo fichero, ponemos

ZipEntry entrada = new ZipEntry("fichero.txt");
os.putNextEntry(entrada);

El nombre que usemos en ZipEntry es el nombre que queramos que tenga el fichero dentro del zip. Aunque es lo habitual, no tiene por qué coincidir con el nombre del fichero fuera del zip. Si queremos que el fichero comprimido esté dentro de un directorio en el zip, bastará con poner también el path donde lo queremos

ZipEntry entrada = new ZipEntry("directorio/fichero.txt");
os.putNextEntry(entrada);

Ahora hay que ir leyendo el fichero normal y pasándole los bytes al ZipOutputStream

FileInputStream fis = new FileInputStream("fichero.txt");
byte [] buffer = new byte[1024];
int leido=0;
while (0 < (leido=fis.read(buffer))){
   os.write(buffer,0,leido);
}

Símplemente abrimos el fichero normal con un FileOutputStream, creamos un buffer de lectura de 1024 bytes (o el tamaño que consideremos adecuado) y nos metemos en un bucle para ir leyendo y escribiendo en el ZipOutputStream.

Una vez que hemos terminado de leer y meter bytes de este fichero, cerramos tanto el fichero como la entrada del zip, indicando así a ZipOutputStream que hemos terminado con este fichero.

fis.close();
os.closeEntry();

repetimos el proceso con todos los ficheros que queramos seguir añadiendo, creando para cada uno de ellos un nuevo ZipEntry, escribiendo los bytes del fichero y cerrando el ZipEntry.

Una vez que acabemos con todos los ficheros, símplemente cerramos el ZipOutputStream

os.close();


Leer y extraer un fichero zip[editar]

Si ya tenemos el fichero zip y lo que queremos es leer su contenido y extraerlo, usaremos la clase ZipInputStream pasándole el fichero zip

ZipInputStream zis = new ZipInputStream(new FileInputStream("fichero.zip"));

Esta clase tiene métodos para consultar cuántas entradas tiene, cuales son, etc. En este ejemplo que pretende ser sencillo, vamos símplemente a ir recorriendo y extrayendo todas las entradas. Para ir recorriendo las entradas

ZipEntry entrada;
while (null != (entrada=zis.getNextEntry()) ){
   System.out.println(entrada.getName());
   ...
}

A cada ZipEntry podremos interrogarle para saber su tamaño, formato de compresión, etc, etc. Basta ver la API de ZipEntry para ver las posibilidades.

Aprovechamos el mismo bucle para ir extrayendo los ficheros

ZipEntry entrada;
while (null != (entrada=zis.getNextEntry()) ){
   System.out.println(entrada.getName());

   FileOutputStream fos = new FileOutputStream(entrada.getName());
   int leido;
   byte [] buffer = new byte[1024];
   while (0<(leido=zis.read(buffer))){
      fos.write(buffer,0,leido);
   }
   fos.close();
   zis.closeEntry();
}

Para cada entrada, creamos un FileOutputStream donde escribiremos el fichero descomprimido. Aquí hemos puesto alegremente new FileOutputStream(entrada.getName()), pero en realidad debemos analizar previamente el entrada.getName() para ver si es el nombre de un fichero válido que podamos escribir. Por ejemplo, si como vimos al escribir el zip, el nombre de la entrada fuera "directorio/fichero.txt", antes de abrir deberíamos crear el directorio u obtendremos un error.

Luego, simplemente se declara un buffer de lectura y nos metemos en un bucle hasta fin de fichero, leyendo del ZipInputStream y escribiendo en el FileOutputStream. Terminado el trasiego, cerramos el FileOutputStream y le indicamos a ZipInputStream que hemos terminado con la entrada, llamando a closeEntry().

Y una vez que salgamos del bucle de entradas (no queden más entradas en el zip), cerramos el ZipOutputStream

zip.close();