Crear archivos ZIP comprimidos con PHP y ZLIB, al vuelo
Crear archivos comprimidos, es otra de las tareas que vienen casi por seguro cuando se trabaja con enormes cantidades de registros y se desea exportar en archivos portables.
En este post mostraré el procedimiento que sigo para crear archivos comprimidos con Gzip, y una pequeña explicación de la clase que utilizo y sus razones:
PHP ofrece la posibilidad de crear archivoz Zip, pero deben ser configurados con diferentes dependencias según la version de PHP. Una de estas librerías es ZipArchive, el cual requiere bastantes dependencias por lo que no me centraré en esta clase (Aunque si alguien está interezado en utilizar esta clase, se podría decir que, la clase que explicaré en este post es una emulación de ZipArchive).
El código y parte de la explicación de esta clase está basado en la explicación que realizan en un artículo de zend.com: Éste clase utiliza el algoritmo de compresión ZLIB compression, que con ayuda de la función pack (que sirve para empaquetar datos en cadenas binarias), complementa la estructura de un archivo Zip. Para una explicación mas a fondo de como y por que lo hace, continuen leyendo en el artículo de zend: zend.com/.../creating-zip-files3 (en ingles).
El código de la clase es el siguiente:
- <?
- class zip{
- var $datasec = array();
- var $ctrl_dir = array();
- var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
- var $old_offset = 0;
- function add_dir($name) {
- $name = str_replace("", "/", $name);
- $fr = "\x50\x4b\x03\x04";
- $fr .= "\x0a\x00";
- $fr .= "\x00\x00";
- $fr .= "\x00\x00";
- $fr .= "\x00\x00\x00\x00";
- $fr .= pack("V",0);
- $fr .= pack("V",0);
- $fr .= pack("V",0);
- $fr .= pack("v", strlen($name) );
- $fr .= pack("v", 0 );
- $fr .= $name;
- $fr .= pack("V", 0);
- $fr .= pack("V", 0);
- $fr .= pack("V", 0);
- $this -> datasec[] = $fr;
- $new_offset = strlen(implode("", $this->datasec));
- $cdrec = "\x50\x4b\x01\x02";
- $cdrec .="\x00\x00";
- $cdrec .="\x0a\x00";
- $cdrec .="\x00\x00";
- $cdrec .="\x00\x00";
- $cdrec .="\x00\x00\x00\x00";
- $cdrec .= pack("V",0);
- $cdrec .= pack("V",0);
- $cdrec .= pack("V",0);
- $cdrec .= pack("v", strlen($name) );
- $cdrec .= pack("v", 0 );
- $cdrec .= pack("v", 0 );
- $cdrec .= pack("v", 0 );
- $cdrec .= pack("v", 0 );
- $ext = "\x00\x00\x10\x00";
- $ext = "\xff\xff\xff\xff";
- $cdrec .= pack("V", 16 );
- $cdrec .= pack("V", $this -> old_offset );
- $cdrec .= $name;
- $this -> ctrl_dir[] = $cdrec;
- $this -> old_offset = $new_offset;
- return;
- }
- function add_fileFromString($data,$name){
- $name = str_replace("", "/", $name);
- $unc_len = strlen($data);
- $crc = crc32($data);
- $zdata = gzcompress($data);
- $zdata = substr ($zdata, 2, -4);
- $c_len = strlen($zdata);
- $fr = "\x50\x4b\x03\x04";
- $fr .= "\x14\x00";
- $fr .= "\x00\x00";
- $fr .= "\x08\x00";
- $fr .= "\x00\x00\x00\x00";
- $fr .= pack("V",$crc);
- $fr .= pack("V",$c_len);
- $fr .= pack("V",$unc_len);
- $fr .= pack("v", strlen($name) );
- $fr .= pack("v", 0 );
- $fr .= $name;
- $fr .= $zdata;
- $fr .= pack("V",$crc);
- $fr .= pack("V",$c_len);
- $fr .= pack("V",$unc_len);
- $this -> datasec[] = $fr;
- $new_offset = strlen(implode("", $this->datasec));
- $cdrec = "\x50\x4b\x01\x02";
- $cdrec .="\x00\x00";
- $cdrec .="\x14\x00";
- $cdrec .="\x00\x00";
- $cdrec .="\x08\x00";
- $cdrec .="\x00\x00\x00\x00";
- $cdrec .= pack("V",$crc);
- $cdrec .= pack("V",$c_len);
- $cdrec .= pack("V",$unc_len);
- $cdrec .= pack("v", strlen($name) );
- $cdrec .= pack("v", 0 );
- $cdrec .= pack("v", 0 );
- $cdrec .= pack("v", 0 );
- $cdrec .= pack("v", 0 );
- $cdrec .= pack("V", 32 );
- $cdrec .= pack("V", $this -> old_offset );
- $this -> old_offset = $new_offset;
- $cdrec .= $name;
- $this -> ctrl_dir[] = $cdrec;
- }
- function add_file($data, $name) {
- $fp = fopen($data,"r");
- $data = fread($fp,filesize($data));
- fclose($fp);
- $name = str_replace("", "/", $name);
- $unc_len = strlen($data);
- $crc = crc32($data);
- $zdata = gzcompress($data);
- $zdata = substr ($zdata, 2, -4);
- $c_len = strlen($zdata);
- $fr = "\x50\x4b\x03\x04";
- $fr .= "\x14\x00";
- $fr .= "\x00\x00";
- $fr .= "\x08\x00";
- $fr .= "\x00\x00\x00\x00";
- $fr .= pack("V",$crc);
- $fr .= pack("V",$c_len);
- $fr .= pack("V",$unc_len);
- $fr .= pack("v", strlen($name) );
- $fr .= pack("v", 0 );
- $fr .= $name;
- $fr .= $zdata;
- $fr .= pack("V",$crc);
- $fr .= pack("V",$c_len);
- $fr .= pack("V",$unc_len);
- $this -> datasec[] = $fr;
- $new_offset = strlen(implode("", $this->datasec));
- $cdrec = "\x50\x4b\x01\x02";
- $cdrec .="\x00\x00";
- $cdrec .="\x14\x00";
- $cdrec .="\x00\x00";
- $cdrec .="\x08\x00";
- $cdrec .="\x00\x00\x00\x00";
- $cdrec .= pack("V",$crc);
- $cdrec .= pack("V",$c_len);
- $cdrec .= pack("V",$unc_len);
- $cdrec .= pack("v", strlen($name) );
- $cdrec .= pack("v", 0 );
- $cdrec .= pack("v", 0 );
- $cdrec .= pack("v", 0 );
- $cdrec .= pack("v", 0 );
- $cdrec .= pack("V", 32 );
- $cdrec .= pack("V", $this -> old_offset );
- $this -> old_offset = $new_offset;
- $cdrec .= $name;
- $this -> ctrl_dir[] = $cdrec;
- }
- function file() {
- $data = implode("", $this -> datasec);
- $ctrldir = implode("", $this -> ctrl_dir);
- return
- $data .
- $ctrldir .
- $this -> eof_ctrl_dir .
- pack("v", sizeof($this -> ctrl_dir)) .
- pack("v", sizeof($this -> ctrl_dir)) .
- pack("V", strlen($ctrldir)) .
- pack("V", strlen($data)) .
- "\x00\x00";
- }
- }
- ?>
Ésta clase contiene los siguiente métodos:
- add_dir($name) - Éste método crea un directorio dentro del archivo zip.
- add_file($data, $name) - Éste método lee un archivo, y lo guarda en el archivo zip.
- add_fileFromString($data,$name) - Éste método lee un una cadena, y la guarda en el archivo que pasemos como parámetro dentro del zip.
- file() - Éste método al ser llamado muestra el contenido del archivo zip. Éste debe ser utilizado para con el contenido que regrese crear un archivo con extensión zip, o para forzar al navegador a que el usuario descargue esa contenido y lo guarde con extensión zip.
Ahora, utilizarla. Es muy sencillo. 3 pasos: 1.- crear instancia de la clase, 2.- agregar información y 3.- crear el archivo zip o al navegador para que sea descargada.
Este ejemplo forza a la descarga directamente en el navegador:
- <?
- $zip = new zip();
- $zip->add_fileFromString("Esto es una prueba\n Estoy probando hola 1, 2, 3 probando... ", "prueba.txt");
- $zip->add_fileFromString("Este es otro archivo comprimido", "prueba2.txt");
- $zip->add_file("/images/foto.jpg","foto.jpg"); //También es posible agregar archivos
- $zip->add_dir("mas_images");//agregar directorio
- $zip->add_file("/images/foto2.jpg","mas_images/foto.jpg");
- $zip->add_file("/images/foto3.jpg","mas_images/foto2.jpg");
- header("Content-type: application/force-download");
- header("Content-Transfer-Encoding: Binary");
- header("Content-length: ".strlen($zip->file()));
- header("Content-Type: application/octet-stream");
- header("Content-Disposition: attachment; filename=\"All-Leagues-{$format}.zip\"");
- echo $zip->file();
- ?>
Este ejemplo guarda el archivo directamente en el disco duro del servidor:
- <?
- $zip = new zip();
- $zip->add_fileFromString("Esto es una prueba\n Estoy probando hola 1, 2, 3 probando... ", "prueba.txt");
- $zip->add_fileFromString("Este es otro archivo comprimido", "prueba2.txt");
- $fileName = substr(md5(rand()),0,5)."-archivo.zip";
- $fd = fopen ($fileName, "wb");
- $out = fwrite ($fd, $zip->file());
- fclose ($fd);
- ?>
mauricio
2009-09-05 18:01:28
Hola vi tu post de buscador con php flavor si es que tienes la documentacion y tutoriales que en la web del proyecto estan caidos te lo agradeceria mucho
saludos
Victor De la Rocha
2009-09-07 11:53:13
Hola mauricio.
Por el momeno no existe documentación acerca de FlavorPHP, dado que aún es muy reciente, y relativamente nuevo incluso para nosotros.
Carlos Varela
2009-10-17 17:20:04
Saludos
He probado el compresor con la clase mencionada.
El problema es que no deja agregar mas de un archivo, pues, al tratar de descomprimirlo genera un mensaje de error.
droope
2010-03-08 14:58:04
wtf??? :P
Raúl
2010-09-11 12:25:32
Hola, muchas gracias por el código me ha ido a la primera, pero una vez descargo el zip se cierra la sesión del navegador.
Mayoral
2010-12-14 23:46:23
Que onda, te dejo un post donde hago mención a algo como esto, espero y pueda servir un poco de apoyo a este post:
http://mayoralito.wordpress.com/2010/12/14/crear-archivo-zip-con-php/
Saludos.
V7bacumbo
2011-03-12 16:05:06
me encanto,es nuy practico...
yerandy
2011-05-16 23:46:45
Hola estoy utilizando esta clase....peor no me deja crear un zip que tenga más de 30MB...como puedo solucionar esto??????
Antonio
2011-06-24 00:07:09
Wow hermano no sabes como me ha servido este codigo, mil gracias!!
Para el que pregunta para agregar mas de un archivo solo ponle un for a la linea add_file asi lo hice yo y no tuve ningun problema.
alma
2011-07-08 15:49:17
hola...
Me podrian decir en donde puedo encontrar
que es un algoritmo porfavor estoy perdida y
eso es una tarea para tecnologia y si no la
entrego pierdo la materia y por ello
el año porfavor ayudenme es solo un simple
favor eso no los va a perjudicares mas seria
algo bueno para todos despues no les perjudica
en nada pero si me ayuda a mi se los agredecere
mucho si me hacen este favos