Hoy toca algo más liviano, los tutoriales de Symfony han sido bastante extensos, no penséis que han acabado, pero voy a intentar llenar un poco el hueco entre ellos… Mientras escribía, he leído un post de Ye Tian para weebly en el que hablan de esta librería, maennchen/ZipStream-PHP. Es una librería que nos permite agregar varios archivos a un comprimido Zip/Tar para su descarga de manera muy sencilla. Plantean un caso bastante simple, en el que vuelcan el contenido de ficheros a variables para ser enviados a descarga, esta librería los comprime en zip/tar y transmite al cliente. Viendo el repositorio de origen entiendo que es posible hacerlo sin tener que consumir memoria con un proceso intermedio. Además es posible empaquetar múltiples ficheros a este comprimido y facilitar en una única descarga todos los elementos que deseemos. Tras las primeras pruebas he comprobado que la librería presenta algunas issues, de manera que el soporte para grandes ficheros (para mi lo más interesante) queda inutilizado. Al hacer stat sobre ficheros de más de dos Gigas parece que se calcula mal el tamaño y lo trata como un archivo pequeño, intentando meterlo en memoria. En Github pude comprobar que no solo falla el stat, si no que la propia compresión Zip tampoco soporta volúmenes de más de 2Gb. Siguiendo los comentarios pude llegar a este “fork” de la herramienta, barracudanetworks/ArchiveStream-php, que si es capaz de realizar la tarea para todos los tamaños de fichero necesarios. Eso si, para los archivos muy grandes no realiza compresión, si no que los almacena sin comprimir en el mismo paquete.

Compresión de ficheros a descargar

La instalación de la librería es sencilla, necesitamos tener la extensión gmp para PHP instalada, añadimos el require de composer, y, al hacer el update ya recibiremos la librería y tendremos actualizado el autoloading.

1
2
3
4
5
{
"require": {
"barracudanetworks/archivestream-php": "1.*"
}
}

Una vez tenemos el vendor ya instalado sera cuestión de hacer la instancia del objeto y añadirle los ficheros necesarios con cualquiera de los métodos facilitados, El propio ArchiveStream se encargara de recoger los datos del fichero y empaquetarlos para su envío. No hace falta que nos preocupemos de más, ya el propio objeto, tras invocar el método finish(), se encargará de setear los headers de descarga y volcar el contenido.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
    require '../vendor/autoload.php';

// Seteamos esto para comprobar el consumo de memoria
ini_set('memory_limit', '128M');

// Cuidado con el execution time
ini_set('max_execution_time', '0');

// Forzamos compresión ZIP
$zip = new \Barracuda\ArchiveStream\ZipArchive('download.zip');

$zip->add_file_from_path('myImage.jpg', '/path/to/files/myImage.jpg');
$zip->add_file_from_path('reallyBigFile.sql', '/another/path/to/files/reallyBigFile.sql');

$zip->finish();

// Si queremos leer un archivo de un contexto diferente se pueden pasar directamente las partes.
//
// $fh = fopen($filePath, 'r');
// $stat = fstat($fh);
// $zip->init_file_stream_transfer($filePath, $stat['size']);
// while (feof($fh) === false) {
// $zip->stream_file_part(fread($fh, 1048576));
// }
// $zip->complete_file_stream();
// $zip->finish();

Realmente sencillo, y la librería es bastante ligera, un buen complemento para simplificar las tareas de descarga de comprimidos en nuestras aplicaciones.