Thu
Jan
04
2007
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);
}
}
Colorized by: CarlosAg.CodeColorizer
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!!");
}
Colorized by: CarlosAg.CodeColorizer
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:\Temp\prueba.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();
}
Colorized by: CarlosAg.CodeColorizer 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:\Temp\walls.zip", @"C:\Wallpapers", false, ".jpg$");
Console.WriteLine("Hecho!!");
}
Colorized by: CarlosAg.CodeColorizer
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.