Comprimiendo y descomprimiendo ficheros ZIP en ASP.NET

Publicado poraleggg | En ASP.NET, Codigo | Posted on 16-04-2009

3

Para comprimir y descomprimir ficheros en formato ZIP tenemos una herramienta muy valiosa a nuestra disposición: la excelente librería SharpZipLib, creada por IC#Code, el mismo grupo de desarrolladores que nos han proporcionado el excelente IDE Open Source para C# SharpDevelop, del que ya os he hablado otras veces.

Lo primero que tenemos que hacer, por lo tanto, es descargar dicha librería a nuestro sistema. Una vez hecho esto, debemos incluirla como referencia en el proyecto de Visual Studio en el que vayamos a usarla. Si creemos que vamos a usarla con relativa frecuencia, lo más cómodo es instalarla en el GAC. Para ello, abrimos la Ventana de comandos de Visual Studio 2005, que encontraremos bajo Herramientas del grupo del menú de Inicio Visual Studio 2005. Cuando lo ejecutamos, se abre una consola que deberemos dirigir a la ruta donde hayamos descargado la librería ICSharpCode.SharpZipLib.dll (por comodidad, yo la tengo en una carpeta propia en C:Archivos de Programa) y una vez situados ahí escribimos el siguiente comando:

gacutil /i ICSharpCode.SharpZipLib.dll

De esta manera añadimos la librería a la caché global de ensamblados para Visual Studio.

Tanto si añadimos la librería al GAC como si no, debemos referenciarla en nuestro proyecto. Para ello, ya sabéis, una vez creado nuestro proyecto (de consola, Windows o Web) elegimos el menú Proyecto, Añadir Referencia y elegimos la librería en el cuadro de diálogo que nos aparece: en la solapa llamada .NET si hemos incluido la librería en el GAC, o podemos buscarla mediante la solapa Examinar. Una vez incluida la referencia, incluimos la línea

using ICSharpCode.SharpZipLib.Zip;

en nuestro código y ya estamos listos.

Una última aclaración antes de meternos en faena: para hacer más simple este artículo he decidido que los ejemplos de código incluidos funcionen en la consola y he dado por supuestas varias cosas, como rutas y nombres de ficheros. Si quieres que el código fuente te funcione no te limites a cortarlo y pegarlo, debes adaptarlo a las rutas y nombres de ficheros existentes en tu sistema.

Listar Contenido de Ficheros

Vamos a ver primero cómo listar el contenido de un fichero ZIP. Para ello, nos limitamos a declarar una variable llamada zip de tipo ZipFile. Nótese que SharpZipLib hace que un ZipFile esté compuesto de objetos ZipEntry, no de ficheros ni de líneas ni de ninguna otra cosa por el estilo. El objeto ZipEntry va a representar cualquiera de las entidades que pueden estar contenidas en un fichero ZIP, sean un fichero o un directorio. Una vez sabido esto, nos recorremos las entradas del fichero ZIP mediante un bucle foreach y sencillamente mostramos el nombre de la entrada en la consola:

private static void ListarContenidoZip(string sFile){

ZipFile zip = new ZipFile(File.OpenRead(sFile));

foreach (ZipEntry entry in zip {

Console.WriteLine(entry.Name);

}}

Listar Contenido de Ficheros
Vamos a ver primero cómo listar el contenido de un fichero ZIP. Para ello, nos limitamos a declarar una variable llamada zip de tipo ZipFile. Nótese que SharpZipLib hace que un ZipFile esté compuesto de objetos ZipEntry, no de ficheros ni de líneas ni de ninguna otra cosa por el estilo. El objeto ZipEntry va a representar cualquiera de las entidades que pueden estar contenidas en un fichero ZIP, sean un fichero o un directorio. Una vez sabido esto, nos recorremos las entradas del fichero ZIP mediante un bucle foreach y sencillamente mostramos el nombre de la entrada en la consola:

Descomprimir Ficheros
La trama se complica. Para descomprimir un fichero ZIP lo abriremos como un objeto ZipInputStream, recorremos sus entradas mediante el método GetNextEntry() y guardaremos el contenido binario de cada entrada en un array de bytes. Dicho array alimentará a un objeto StreamWriter, con el cual crearemos el nuevo fichero, que será una copia exacta de los datos binarios de la entrada del fichero ZIP.

private static void DescomprimirZip(string sFile)
{
ZipInputStream zipIn = new ZipInputStream(File.OpenRead(sFile));
ZipEntry entry;
while ((entry = zipIn.GetNextEntry()) != null)
{
FileStream streamWriter = File.Create(@”C:Temp” + entry.Name);
long size = entry.Size;
byte[] data = new byte[size];
while (true)
{
size = zipIn.Read(data, 0, data.Length);
if (size > 0) streamWriter.Write(data, 0, (int) size);
else break;
}
streamWriter.Close();
}
Console.WriteLine(“Hecho!!”);
}

Comprimir ficheros
Para comprimir ficheros, usaremos un objeto similar al anterior pero de propósito opuesto, el ZipOutputStream. Para mantener la sencillez del ejemplo, he supuesto que queremos comprimir todos los ficheros existentes en la ruta pasada en el parámetro sRuta. Por lo tanto, nos recorremos todos los ficheros existentes en dicha ruta, creamos un nuevo objeto ZipEntry, obtenemos cierta información mediante FileInfo (podríamos haber incluido chequeo de CRC, pero quería mantener el ejemplo sencillo) para asignar dicha información a la nueva entrada, y añadimos la entrada al objeto ZipOutputStream mediante el método PutNextEntry. Ojo, esto crearía una entrada vacía: para realmente introducir los datos que contiene el fichero (sea un fichero de texto o un binario cualquiera) debemos leer sus datos binarios mediante un FileStream como en el ejemplo anterior y entonces invocar el método Write del ZipOutputStream para guardar dichos datos binarios en la última entrada del stream de salida Zip. Una vez recorridos e incluidos todos los ficheros, terminamos y cerramos el objeto ZipOutputStream.

private static void ComprimirZip(string sRuta)
{
ZipOutputStream zipOut  = new ZipOutputStream(File.Create(@”C:Tempprueba.zip”));
foreach(string fName in Directory.GetFiles(sRuta))
{
FileInfo fi  = new FileInfo(fName);
ZipEntry entry = new ZipEntry(fi.Name);
FileStream sReader = File.OpenRead(fName);
byte[] buff = new byte[Convert.ToInt32(sReader.Length)];
sReader.Read(buff, 0, (int) sReader.Length);
entry.DateTime = fi.LastWriteTime;
entry.Size = sReader.Length;
sReader.Close();
zipOut.PutNextEntry(entry);
zipOut.Write(buff, 0, buff.Length);
}
zipOut.Finish();
zipOut.Close();
}

Y esto concluye esta pequeña introducción a SharpZipLib. IC#Code mantiene un excelente foro (en inglés) en el que podréis ver más ejemplo de código y más dudas resueltas.

Actualización 5/01/2007.- Jugando un poco con la librería y el Examinador de Objetos de VS 2005, me encuentro con una clase llamada FastZip. Hmmmm. Pues sí, es lo que parece: una clase wrapper para facilitar la compresión y descompresión de archivos mediante SharpZipLib. Por ejemplo, para descomprimir un fichero es tan simple como esto:

private static void DescomprimirZipFast(string sFile)
{
FastZip fZip = new FastZip();
fZip.ExtractZip(sFile, @”C:Temp”, “”);
}
Colorized by: CarlosAg.CodeColorizer
Los tres argumentos del método ExtractZip son de tipo string, y son el nombre del fichero ZIP a extraer, la ruta de destino para los ficheros extraídos y una máscara para el tipo de ficheros que debemos extraer. Ojo, no vale una máscara al estilo “*.txt”, debe hacerse mediante una expresión regular. Si queremos incluir todos los archivos existentes en el directorio, debemos dejar este último parámetro como null o como cadena vacía “”.

Y para comprimir:

private static void ComprimirZipFast()
{
FastZip fZip = new FastZip();
fZip.CreateZip(@”C:Tempwalls.zip”, @”C:Wallpapers”, false, “.jpg$”);
Console.WriteLine(“Hecho!!”);
}
En este caso el método CreateZip admite cuatro argumentos: el nombre y ruta del fichero ZIP a crear, el directorio donde se encuentran los ficheros que queremos comprimir, un parámetro booleano que indica si la compresión es recursiva (incluimos subdirectorios) o no, y por último un parámetro de tipo string que contiene la máscara para elegir el tipo de ficheros que queremos a comprimir. En este caso es todos los ficheros con extensión JPG. Se aplican las mismas excepciones en uso de máscaras que he comentado para ExtractZip.

Autor