Guía 54 – Utilización de ficheros ZIP para transportar contenidos

En muchas ocasiones, en mi trabajo, hemos tenido que trasladar gran volumen de información de unos sistemas a otros (estando desconectados ambos equipos) por lo que no nos queda más remedio que obtener ficheros y moverlos de una máquina a otra.
Normalmente no era información de un único fichero de tipo TXT, si no que eran varios y además podían tener información en formato binario.

Así pues, para moverlos utilizábamos el formato ZIP, como contenedor y compresor de la información.

Para ilustrar la funcionalidad he definido un caso en donde se crean los usuarios y mediante selección de estos, se extraen sus datos e imágenes en un fichero ZIP y cómo a través de ese fichero ZIP se hace una carga incremental de los datos, en el sistema destino. Para simplificar el ejemplo he incluido las 2 funcionalidad en el mismo aplicativo de PHPRunner.

Creo que existen muchas más casuística en donde se puede aplicar este ejemplo. Lo importante, es conocer que mediante una simple programación, esto es posible con PHPRunner.

Objetivo

Obtener conocimientos de cómo hacer exportaciones de datos (texto, números, fechas y binario) y guardarlos en un fichero ZIP para entregar a un usuario o para aplicar a otra aplicación.
y cómo podemos cargar datos almacenados en ficheros ZIP, en nuestra base de datos.

DEMO: https://fhumanes.com/zip_manager

Solución Técnica

Para la gestión de los ficheros ZIP utilizaremos las funciones que incorpora el propio PHP.

Para intentar explicar con claridad el código desarrollado explicaré las 2 funcionalidades por separado:

Extraer Datos (crear fichero ZIP)

La aplicación muestra este interfaz:

En el interfaz de la página LIST se selecciona los registros que se quieren exportar y se pulsa el botón «Export User», en ese instante se ejecuta el código:

<?php 
@ini_set("display_errors","1");
@ini_set("display_startup_errors","1");

// Delete all ile temporal
function deleteAllFilesToDirectory(string $directory){
    $dir = $directory;
    if(file_exists($dir)){
        $di = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS);
        $ri = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::CHILD_FIRST);
        foreach ( $ri as $file ) {
            $file->isDir() ?  rmdir($file) : unlink($file);  
            // if ( $file->isDir()) {  echo "dir.: ".$file."\n"; } else { echo "file: ".$file."\n"; }
        }
    }   
}

require_once(__DIR__."/../include/dbcommon.php");

$ids_arr = $_SESSION['ids'];  // Recupera los ID seleccionados

$content_arr = array();
$list_id = implode(',',$ids_arr);
$sql = "SELECT * FROM zip_user WHERE ID IN($list_id)";
$rs = DB::Query($sql);
while( $data = $rs->fetchAssoc() )  // Load all record in Array
{
    $data['image'] = base64_encode($data['image']);
    $content_arr[] = $data;
}

$temp_directory = tempnam(sys_get_temp_dir(), 'exp');
unlink($temp_directory); // Elimino fichero temporal
$temp_directory =str_replace('.','_',$temp_directory);    // Quito "." en Windows

$temp_zip = str_replace('_tmp','',$temp_directory).".zip";   // Name file ZIP
mkdir($temp_directory,0700);  // directorio de exportación
$temp_directory_image = $temp_directory."/image";
mkdir($temp_directory_image,0700);  // directorio de imágenes
$error = error_get_last();
// Create images all record select
foreach ($content_arr as &$record) {
    $ext_file = explode('.',$record['filename']);
    $id = $record['id'];
    $temp_file = $temp_directory_image."/image_$id.".$ext_file[1];
    $fp = fopen($temp_file , 'wb');
    fwrite($fp, base64_decode($record['image']));
    fclose($fp);
}
// Create file CSV

$tmp_csv = $temp_directory."/export.csv";
$csv_arr = array();
$csv_arr[] = array('id','name','addDate','precio','filename');
// Format
$decimal = new \NumberFormatter("es-ES", \NumberFormatter::DECIMAL); 
$decimal->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, 2);
$decimal->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, 2); // by default some locales got max 2 fraction digits
$entero = new \NumberFormatter("es-ES", \NumberFormatter::DECIMAL);
$entero->setAttribute(\NumberFormatter::MIN_FRACTION_DIGITS, 0);
$entero->setAttribute(\NumberFormatter::MAX_FRACTION_DIGITS, 0); 

// 
foreach ($content_arr as &$record) {
    $csv_arr[] = array($record['id'],$record['name'],date_format(date_create($record['addDate']),"d/m/Y"),
                 $decimal->format($record['precio']),$record['filename']);
}
$fp = fopen($tmp_csv, 'w');
foreach ($csv_arr as $line) {  // Write file CSV
    fputcsv($fp, $line);
}
fclose($fp);

// Create zip file
$zip = new ZipArchive();

if ($zip->open($temp_zip, ZipArchive::CREATE)!==TRUE) {
    exit("cannot open <$temp_zip>\n");
}
$zip->addFile($temp_directory."/export.csv","export.csv");
$options = array('add_path' => 'image/', 'remove_all_path' => TRUE);
$zip->addGlob($temp_directory_image.'/*.*', GLOB_BRACE, $options);
$zip->close();
// Download zip
$content_zip = file_get_contents($temp_zip);

deleteAllFilesToDirectory($temp_directory);  // Delete All file temporal
rmdir($temp_directory);
unlink($temp_zip);  // delete file zip

header("Content-Disposition: attachment; filename= export.zip");
header('Content-Type: application/zip');
echo $content_zip;

Y se crea el fichero «export.zip» que contiene un fichero «export.csv» (con la información de los registros menos las imágenes) y el directorio «images» que contiene los ficheros de imágenes del usuario (la identificación del fichero se hace con el valor del campo ID).

Importar Datos (leer fichero ZIP)

La aplicación muestra este interfaz:

Se utiliza la información del campo «status» para saber si el fichero está aplicado (cargado) y si hay incidentes en el proceso de carga, quedará informado en el campo «comment».

Para ejecutar el proceso de importación se debe pulsar el botón «Import User» (sólo está disponible en los registros donde el «status» está con el valor «0».

El código que se ejecuta es:

<?php
// $data = $button->getCurrentRecord();  // this in code by button

// Delete all ile temporal
function deleteAllFilesToDirectory(string $directory){
    $dir = $directory;
    if(file_exists($dir)){
        $di = new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS);
        $ri = new RecursiveIteratorIterator($di, RecursiveIteratorIterator::CHILD_FIRST);
        foreach ( $ri as $file ) {
            $file->isDir() ?  rmdir($file) : unlink($file);  
            // if ( $file->isDir()) {  echo "dir.: ".$file."\n"; } else { echo "file: ".$file."\n"; }
        }
    }   
}

$comment = '';

// Create temporal files and Dir.
$temp_directory = tempnam(sys_get_temp_dir(), 'imp');
unlink($temp_directory); // Elimino fichero temporal
$temp_directory =str_replace('.','_',$temp_directory);    // Quito "." en Windows

$temp_zip = str_replace('_tmp','',$temp_directory).".zip";   // Name file ZIP
mkdir($temp_directory,0700);  // directorio de exportación
$temp_directory_image = $temp_directory."/image";
$temp_csv = $temp_directory."/export.csv";                  // Name file CSV

// Create Zip file
$fpz = fopen($temp_zip, 'w');
fwrite($fpz,$data['archive']);
fclose($fpz);

// Extract ZIP file
$za = new ZipArchive();
$za->open($temp_zip);
$za->extractTo($temp_directory.'/');
$za->close();

// Load file csv
$data_arr = array();
$row = 1;
if (($handle = fopen($temp_csv , "r")) !== FALSE) {
    while (($data2 = fgetcsv($handle, 0, ",")) !== FALSE) {
        $num = count($data2);
        $commnet .= "  $num fields in line $row \n";
        if ($row == 1){  // Head
            $data_head = $data2;
        } else {
            for ($c=0; $c < $num; $c++) {
                $data_arr[$row][$data_head[$c]] = $data2[$c];
            }
        }
        $row++;
    }
    fclose($handle);
}

// Manager All new record by file CSV
foreach ($data_arr as &$new_record) {

    $image = Null;
    $name_file = $temp_directory_image."/image_".$new_record['id'];
    foreach (glob($name_file .".*") as $temp_file) {
        $image = file_get_contents($temp_file);  
    }

    // Prepare INSERT new Record
    // All fields: name, addDate, precio, image, filename

    $data3 = array();
    $data3["name"] = $new_record['name'];
    $date_aux = DateTime::createFromFormat('d/m/Y', $new_record['addDate']);
    $error = DateTime::getLastErrors();
    $data3["addDate"]  = date_format($date_aux,'Y-m-d');
    $data3["precio"] =  floatval(str_replace(',', '.', str_replace('.', '', $new_record['precio'])));
    $data3["image"] = $image;
    $data3["filename"] = $new_record['filename'];

    DB::Insert("zip_user", $data3);  // Insert new record in data base
    if (DB::LastError() <> ''){
        $comment .= DB::LastError()."in record with ID = ".$new_record['id']."\n";
    }
}

// Delete Temporal files
deleteAllFilesToDirectory($temp_directory);  // Delete All file temporal
rmdir($temp_directory);
unlink($temp_zip);  // delete file zip

// Update Archive
$data4 = array();
$keyvalues = array();
$data4["comment"] = $comment;
$data4["status"]  = "1";
$keyvalues["id"] = $data['id'];
DB::Update("zip_archive", $data4, $keyvalues );

La exportación e importación de información es muy variada, por lo que espero que este ejemplo os facilite la programación.

Para cualquier duda, contactar conmigo a través de mi email [email protected]

Como siempre, os dejo el ejemplo con todos los ficheros para que lo podáis instalar, probar y cambiar en vuestros equipos.

Adjuntos

Archivo Tamaño de archivo Descargas
zip PHPrunner 10.7 + backup BD - actualizado 20/03/2023 2 MB 129

Blog personal para facilitar soporte gratuito a usuarios de React y PHPRunner