Cómo comprimir una carpeta completa usando PHP

132

He encontrado aquí en stackoveflow algunos códigos sobre cómo comprimir un archivo específico, pero ¿qué tal una carpeta específica?

Folder/
  index.html
  picture.jpg
  important.txt

adentro adentro My Folder, hay archivos. después de comprimir el My Folder, también quiero eliminar todo el contenido de la carpeta, excepto important.txt.

Encontré esto aquí en la pila

Necesito tu ayuda. Gracias.

woninana
fuente
Hasta donde puedo ver, el enlace de stackoverflow que ha proporcionado realmente comprime múltiples archivos. ¿Con qué parte tienes problemas?
Lasse Espeholt
@lasseespeholt El enlace que he dado cremalleras solamente un archivo específico, no la carpeta y el contenido de la carpeta ..
woninana
Toma una matriz de archivos (esencialmente una carpeta) y agrega todos los archivos al archivo zip (el bucle). Puedo ver que se ha publicado una buena respuesta ahora +1 :), que es el mismo código, la matriz es solo una lista de archivos de un directorio ahora.
Lasse Espeholt
posible duplicado de la carpeta comprimir / archivar usando script php
Pekka

Respuestas:

321

Código actualizado el 22/04/2015.

Comprime una carpeta completa:

// Get real path for our folder
$rootPath = realpath('folder-to-zip');

// Initialize archive object
$zip = new ZipArchive();
$zip->open('file.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);

// Create recursive directory iterator
/** @var SplFileInfo[] $files */
$files = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($rootPath),
    RecursiveIteratorIterator::LEAVES_ONLY
);

foreach ($files as $name => $file)
{
    // Skip directories (they would be added automatically)
    if (!$file->isDir())
    {
        // Get real and relative path for current file
        $filePath = $file->getRealPath();
        $relativePath = substr($filePath, strlen($rootPath) + 1);

        // Add current file to archive
        $zip->addFile($filePath, $relativePath);
    }
}

// Zip archive will be created only after closing object
$zip->close();

Comprima una carpeta completa + elimine todos los archivos excepto "important.txt":

// Get real path for our folder
$rootPath = realpath('folder-to-zip');

// Initialize archive object
$zip = new ZipArchive();
$zip->open('file.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);

// Initialize empty "delete list"
$filesToDelete = array();

// Create recursive directory iterator
/** @var SplFileInfo[] $files */
$files = new RecursiveIteratorIterator(
    new RecursiveDirectoryIterator($rootPath),
    RecursiveIteratorIterator::LEAVES_ONLY
);

foreach ($files as $name => $file)
{
    // Skip directories (they would be added automatically)
    if (!$file->isDir())
    {
        // Get real and relative path for current file
        $filePath = $file->getRealPath();
        $relativePath = substr($filePath, strlen($rootPath) + 1);

        // Add current file to archive
        $zip->addFile($filePath, $relativePath);

        // Add current file to "delete list"
        // delete it later cause ZipArchive create archive only after calling close function and ZipArchive lock files until archive created)
        if ($file->getFilename() != 'important.txt')
        {
            $filesToDelete[] = $filePath;
        }
    }
}

// Zip archive will be created only after closing object
$zip->close();

// Delete all files from "delete list"
foreach ($filesToDelete as $file)
{
    unlink($file);
}
Dador
fuente
2
Debe establecer chmod (de escritura) en el directorio (donde se encuentra este script) en 777. Por ejemplo: si el script se encuentra en /var/www/localhost/script.php, entonces debe configurar chmod 0777 en dir / var / www / localhost /.
Dador
3
Eliminar los archivos antes de llamar $zip->close()no funcionará. Comprueba mi respuesta aquí
hek2mgl
10
@alnassre ese es el requisito de la pregunta: "También quiero eliminar todo el contenido de la carpeta, excepto important.txt". También te aconsejo que siempre leas el código antes de ejecutarlo.
Dador
1
@alnassre jajajaja ... lo siento :) ... jajaja
Ondrej Rafaj
1
@ nick-newman, sí, para calcular el porcentaje puede usar php.net/manual/ru/function.iterator-count.php + contador dentro del bucle. En cuanto al nivel de compresión, no es posible con ZipArchive en este momento: stackoverflow.com/questions/1833168/…
Dador
54

Hay un método indocumentado útil en la clase ZipArchive: addGlob ();

$zipFile = "./testZip.zip";
$zipArchive = new ZipArchive();

if ($zipArchive->open($zipFile, (ZipArchive::CREATE | ZipArchive::OVERWRITE)) !== true)
    die("Failed to create archive\n");

$zipArchive->addGlob("./*.txt");
if ($zipArchive->status != ZIPARCHIVE::ER_OK)
    echo "Failed to write files to zip\n";

$zipArchive->close();

Ahora documentado en: www.php.net/manual/en/ziparchive.addglob.php

Mark Baker
fuente
2
@netcoder: los beneficios de haber escrito el phpt para probarlo ... básicamente, leyeron el código fuente de la clase ZipArchive y lo encontraron allí ... también hay un método addPattern () indocumentado que toma un patrón de estilo regexp, pero nunca he logrado que funcione (puede ser un error en la clase)
Mark Baker
1
@kread: puede usar esto con cualquier lista de archivos que se pueda extraer con glob (), por lo que lo he encontrado extremadamente útil desde que lo descubrí.
Mark Baker,
@ MarkBaker Sé que este comentario llega años después de que publicaste, solo estoy probando suerte aquí. He publicado una pregunta aquí sobre la compresión también. Estoy a punto de probar el método global que publicaste aquí, pero mi problema principal es que no puedo usar addFromString y he estado usando addFile, que está fallando en silencio. ¿Quizás tienes alguna idea de lo que podría estar yendo mal o lo que podría estar haciendo mal?
Skytiger
@ user1032531: la última línea de mi publicación (editada el 13 de diciembre de 2013) indica exactamente eso, con un enlace a la página de documentos
Mark Baker,
66
Es addGlobrecursivo?
Vincent Poirier
20

Prueba esto:

$zip = new ZipArchive;
$zip->open('myzip.zip', ZipArchive::CREATE);
foreach (glob("target_folder/*") as $file) {
    $zip->addFile($file);
    if ($file != 'target_folder/important.txt') unlink($file);
}
$zip->close();

Sin embargo, esto no se comprimirá recursivamente.

netcoder
fuente
Seguro que elimina algunos archivos My folder, pero también tengo una carpeta dentro de una carpeta My folderque me da un error de: Permiso denegado al desvincular la carpeta con inMy folder
woninana
@Stupefy: intente en su if (!is_dir($file) && $file != 'target_folder...')lugar. O marque @kread answer si desea comprimir recursivamente, es la forma más eficiente.
netcoder
La carpeta dentro de My foldertodavía no se elimina, pero de todos modos no hay más errores.
woninana
También olvidé mencionar que no hay archivos .zip creados.
woninana
1
Eliminar los archivos antes de llamar $zip->close()no funcionará. Comprueba mi respuesta aquí
hek2mgl
19

Supongo que esto se está ejecutando en un servidor donde la aplicación zip está en la ruta de búsqueda. Debería ser cierto para todos los servidores basados ​​en Unix y supongo que la mayoría de los servidores basados ​​en Windows.

exec('zip -r archive.zip "My folder"');
unlink('My\ folder/index.html');
unlink('My\ folder/picture.jpg');

El archivo residirá en archive.zip después. Tenga en cuenta que los espacios en blanco en los nombres de archivos o carpetas son una causa común de errores y deben evitarse siempre que sea posible.

Kevin Read
fuente
15

Intenté con el código a continuación y está funcionando. El código se explica por sí mismo, avíseme si tiene alguna pregunta.

<?php
class FlxZipArchive extends ZipArchive 
{
 public function addDir($location, $name) 
 {
       $this->addEmptyDir($name);
       $this->addDirDo($location, $name);
 } 
 private function addDirDo($location, $name) 
 {
    $name .= '/';
    $location .= '/';
    $dir = opendir ($location);
    while ($file = readdir($dir))
    {
        if ($file == '.' || $file == '..') continue;
        $do = (filetype( $location . $file) == 'dir') ? 'addDir' : 'addFile';
        $this->$do($location . $file, $name . $file);
    }
 } 
}
?>

<?php
$the_folder = '/path/to/folder/to/be/zipped';
$zip_file_name = '/path/to/zip/archive.zip';
$za = new FlxZipArchive;
$res = $za->open($zip_file_name, ZipArchive::CREATE);
if($res === TRUE) 
{
    $za->addDir($the_folder, basename($the_folder));
    $za->close();
}
else{
echo 'Could not create a zip archive';
}
?>
Amir Md Amiruzzaman
fuente
Excelente solucion. Funciona en laravel 5.5 también. Realmente me gustó eso. (y)
Web Artisan
1
Gran código! ¡Limpio, simple y funciona perfectamente! ;) Me parece la mejor respuesta. Si puede ayudar a alguien: acabo de agregar ini_set('memory_limit', '512M');antes de la ejecución del script y ini_restore('memory_limit');al final. Era necesario evitar la falta de memoria en el caso de carpetas pesadas (era una carpeta de más de 500 MB).
Jacopo Pace
1
En mi entorno (PHP 7.3, Debian) se creó un archivo ZIP sin una lista de directorios (archivo grande y vacío). Tuve que cambiar la siguiente línea: $ name. = '/'; en $ name = ($ name == '.'? '': $ name. '/');
Gerfried
Esto es trabajo para mí. Gracias por compartir. ¡Salud!
Sathiska
8

Esta es una función que comprime una carpeta completa y su contenido en un archivo zip y puede usarlo de la siguiente manera:

addzip ("path/folder/" , "/path2/folder.zip" );

función:

// compress all files in the source directory to destination directory 
    function create_zip($files = array(), $dest = '', $overwrite = false) {
    if (file_exists($dest) && !$overwrite) {
        return false;
    }
    if (($files)) {
        $zip = new ZipArchive();
        if ($zip->open($dest, $overwrite ? ZIPARCHIVE::OVERWRITE : ZIPARCHIVE::CREATE) !== true) {
            return false;
        }
        foreach ($files as $file) {
            $zip->addFile($file, $file);
        }
        $zip->close();
        return file_exists($dest);
    } else {
        return false;
    }
}

function addzip($source, $destination) {
    $files_to_zip = glob($source . '/*');
    create_zip($files_to_zip, $destination);
    echo "done";
}
Alireza Fallah
fuente
¿Cómo incluir subcarpetas también en la copia de seguridad automáticamente con este script? @Alireza
floCoder
2

¿Por qué no probar EFS PhP-ZiP MultiVolume Script ? Comprimí y transferí cientos de conciertos y millones de archivos ... ssh es necesario para crear archivos de manera efectiva.

Pero creo que los archivos resultantes se pueden usar con exec directamente desde php:

exec('zip -r backup-2013-03-30_0 . -i@backup-2013-03-30_0.txt');

No sé si funciona. No lo he probado ...

"el secreto" es que el tiempo de ejecución para el archivo no debe exceder el tiempo permitido para la ejecución del código PHP.

PorREV
fuente
1

Este es un ejemplo de trabajo de hacer ZIP en PHP:

$zip = new ZipArchive();
$zip_name = time().".zip"; // Zip name
$zip->open($zip_name,  ZipArchive::CREATE);
foreach ($files as $file) {
  echo $path = "uploadpdf/".$file;
  if(file_exists($path)){
  $zip->addFromString(basename($path),  file_get_contents($path));---This is main function  
  }
  else{
   echo"file does not exist";
  }
}
$zip->close();
Amor del sol
fuente
1

Encontré esta publicación en Google como el segundo resultado principal, primero estaba usando exec :(

De todos modos, aunque esto no satisfizo mis necesidades exactamente ... Decidí publicar una respuesta para otros con mi versión rápida pero extendida de esto.

CARACTERÍSTICAS DE LA ESCRITURA

  • Nombre de archivo de respaldo día a día, PREFIX-AAAA-MM-DD-POSTFIX.EXTENSION
  • Informes de archivos / faltantes
  • Listado de copias de seguridad anteriores
  • No comprime / incluye copias de seguridad anteriores;)
  • Funciona en windows / linux

De todos modos, en el script ... Si bien puede parecer mucho ... Recuerde que hay un exceso aquí ... Así que siéntase libre de eliminar las secciones de informes según sea necesario ...

También puede parecer desordenado y ciertas cosas podrían limpiarse fácilmente ... Así que no comente al respecto, es solo un guión rápido con comentarios básicos incluidos ... NO PARA USO EN VIVO ... Pero fácil de limpiar para uso en vivo !

En este ejemplo, se ejecuta desde un directorio que está dentro de la carpeta raíz www / public_html. Por lo tanto, solo necesita subir una carpeta para llegar a la raíz.

<?php
    // DIRECTORY WE WANT TO BACKUP
    $pathBase = '../';  // Relate Path

    // ZIP FILE NAMING ... This currently is equal to = sitename_www_YYYY_MM_DD_backup.zip 
    $zipPREFIX = "sitename_www";
    $zipDATING = '_' . date('Y_m_d') . '_';
    $zipPOSTFIX = "backup";
    $zipEXTENSION = ".zip";

    // SHOW PHP ERRORS... REMOVE/CHANGE FOR LIVE USE
    ini_set('display_errors',1);
    ini_set('display_startup_errors',1);
    error_reporting(-1);




// ############################################################################################################################
//                                  NO CHANGES NEEDED FROM THIS POINT
// ############################################################################################################################

    // SOME BASE VARIABLES WE MIGHT NEED
    $iBaseLen = strlen($pathBase);
    $iPreLen = strlen($zipPREFIX);
    $iPostLen = strlen($zipPOSTFIX);
    $sFileZip = $pathBase . $zipPREFIX . $zipDATING . $zipPOSTFIX . $zipEXTENSION;
    $oFiles = array();
    $oFiles_Error = array();
    $oFiles_Previous = array();

    // SIMPLE HEADER ;)
    echo '<center><h2>PHP Example: ZipArchive - Mayhem</h2></center>';

    // CHECK IF BACKUP ALREADY DONE
    if (file_exists($sFileZip)) {
        // IF BACKUP EXISTS... SHOW MESSAGE AND THATS IT
        echo "<h3 style='margin-bottom:0px;'>Backup Already Exists</h3><div style='width:800px; border:1px solid #000;'>";
            echo '<b>File Name: </b>',$sFileZip,'<br />';
            echo '<b>File Size: </b>',$sFileZip,'<br />';
        echo "</div>";
        exit; // No point loading our function below ;)
    } else {

        // NO BACKUP FOR TODAY.. SO START IT AND SHOW SCRIPT SETTINGS
        echo "<h3 style='margin-bottom:0px;'>Script Settings</h3><div style='width:800px; border:1px solid #000;'>";
            echo '<b>Backup Directory: </b>',$pathBase,'<br /> ';
            echo '<b>Backup Save File: </b>',$sFileZip,'<br />';
        echo "</div>";

        // CREATE ZIPPER AND LOOP DIRECTORY FOR SUB STUFF
        $oZip = new ZipArchive;
        $oZip->open($sFileZip,  ZipArchive::CREATE | ZipArchive::OVERWRITE);
        $oFilesWrk = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($pathBase),RecursiveIteratorIterator::LEAVES_ONLY);
        foreach ($oFilesWrk as $oKey => $eFileWrk) {
            // VARIOUS NAMING FORMATS OF THE CURRENT FILE / DIRECTORY.. RELATE & ABSOLUTE
            $sFilePath = substr($eFileWrk->getPathname(),$iBaseLen, strlen($eFileWrk->getPathname())- $iBaseLen);
            $sFileReal = $eFileWrk->getRealPath();
            $sFile = $eFileWrk->getBasename();

            // WINDOWS CORRECT SLASHES
            $sMyFP = str_replace('\\', '/', $sFileReal);

            if (file_exists($sMyFP)) {  // CHECK IF THE FILE WE ARE LOOPING EXISTS
                if ($sFile!="."  && $sFile!="..") { // MAKE SURE NOT DIRECTORY / . || ..
                    // CHECK IF FILE HAS BACKUP NAME PREFIX/POSTFIX... If So, Dont Add It,, List It
                    if (substr($sFile,0, $iPreLen)!=$zipPREFIX && substr($sFile,-1, $iPostLen + 4)!= $zipPOSTFIX.$zipEXTENSION) {
                        $oFiles[] = $sMyFP;                     // LIST FILE AS DONE
                        $oZip->addFile($sMyFP, $sFilePath);     // APPEND TO THE ZIP FILE
                    } else {
                        $oFiles_Previous[] = $sMyFP;            // LIST PREVIOUS BACKUP
                    }
                }
            } else {
                $oFiles_Error[] = $sMyFP;                       // LIST FILE THAT DOES NOT EXIST
            }
        }
        $sZipStatus = $oZip->getStatusString();                 // GET ZIP STATUS
        $oZip->close(); // WARNING: Close Required to append files, dont delete any files before this.

        // SHOW BACKUP STATUS / FILE INFO
        echo "<h3 style='margin-bottom:0px;'>Backup Stats</h3><div style='width:800px; height:120px; border:1px solid #000;'>";
            echo "<b>Zipper Status: </b>" . $sZipStatus . "<br />";
            echo "<b>Finished Zip Script: </b>",$sFileZip,"<br />";
            echo "<b>Zip Size: </b>",human_filesize($sFileZip),"<br />";
        echo "</div>";


        // SHOW ANY PREVIOUS BACKUP FILES
        echo "<h3 style='margin-bottom:0px;'>Previous Backups Count(" . count($oFiles_Previous) . ")</h3><div style='overflow:auto; width:800px; height:120px; border:1px solid #000;'>";
        foreach ($oFiles_Previous as $eFile) {
            echo basename($eFile) . ", Size: " . human_filesize($eFile) . "<br />";
        }
        echo "</div>";

        // SHOW ANY FILES THAT DID NOT EXIST??
        if (count($oFiles_Error)>0) {
            echo "<h3 style='margin-bottom:0px;'>Error Files, Count(" . count($oFiles_Error) . ")</h3><div style='overflow:auto; width:800px; height:120px; border:1px solid #000;'>";
            foreach ($oFiles_Error as $eFile) {
                echo $eFile . "<br />";
            }
            echo "</div>";
        }

        // SHOW ANY FILES THAT HAVE BEEN ADDED TO THE ZIP
        echo "<h3 style='margin-bottom:0px;'>Added Files, Count(" . count($oFiles) . ")</h3><div style='overflow:auto; width:800px; height:120px; border:1px solid #000;'>";
        foreach ($oFiles as $eFile) {
            echo $eFile . "<br />";
        }
        echo "</div>";

    }


    // CONVERT FILENAME INTO A FILESIZE AS Bytes/Kilobytes/Megabytes,Giga,Tera,Peta
    function human_filesize($sFile, $decimals = 2) {
        $bytes = filesize($sFile);
        $sz = 'BKMGTP';
        $factor = floor((strlen($bytes) - 1) / 3);
        return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$sz[$factor];
    }
?>

¿¿QUÉ HACE??

Simplemente comprimirá el contenido completo de la variable $ pathBase y almacenará el zip en esa misma carpeta. Realiza una detección simple de copias de seguridad anteriores y las omite.

CRON BACKUP

Esta secuencia de comandos que acabo de probar en Linux y funcionó bien desde un trabajo cron con el uso de una URL absoluta para el pathBase.

Enojado 84
fuente
También he excluido el script de eliminación, puede ver la respuesta aceptada para esto
Angry 84
Tengo que amar esos votos al azar sin un comentario que explique por qué.
Enojado 84
1

Utiliza esta función:

function zip($source, $destination)
{
    if (!extension_loaded('zip') || !file_exists($source)) {
        return false;
    }

    $zip = new ZipArchive();
    if (!$zip->open($destination, ZIPARCHIVE::CREATE)) {
        return false;
    }

    $source = str_replace('\\', '/', realpath($source));

    if (is_dir($source) === true) {
        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);

        foreach ($files as $file) {
            $file = str_replace('\\', '/', $file);

            // Ignore "." and ".." folders
            if (in_array(substr($file, strrpos($file, '/')+1), array('.', '..'))) {
                continue;
            }               

            $file = realpath($file);

            if (is_dir($file) === true) {
                $zip->addEmptyDir(str_replace($source . '/', '', $file . '/'));
            } elseif (is_file($file) === true) {
                $zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
            }
        }
    } elseif (is_file($source) === true) {
        $zip->addFromString(basename($source), file_get_contents($source));
    }

    return $zip->close();
}

Ejemplo de uso:

zip('/folder/to/compress/', './compressed.zip');
Waqar Alamgir
fuente
1

Use esto está funcionando bien.

$dir = '/Folder/';
$zip = new ZipArchive();
$res = $zip->open(trim($dir, "/") . '.zip', ZipArchive::CREATE | ZipArchive::OVERWRITE);
if ($res === TRUE) {
    foreach (glob($dir . '*') as $file) {
        $zip->addFile($file, basename($file));
    }
    $zip->close();
} else {
    echo 'Failed to create to zip. Error: ' . $res;
}
Indrajeet Singh
fuente
1

Crea una carpeta zip en PHP.

Método de creación de zip

   public function zip_creation($source, $destination){
    $dir = opendir($source);
    $result = ($dir === false ? false : true);

    if ($result !== false) {

        
        $rootPath = realpath($source);
         
        // Initialize archive object
        $zip = new ZipArchive();
        $zipfilename = $destination.".zip";
        $zip->open($zipfilename, ZipArchive::CREATE | ZipArchive::OVERWRITE );
         
        // Create recursive directory iterator
        /** @var SplFileInfo[] $files */
        $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($rootPath), RecursiveIteratorIterator::LEAVES_ONLY);
         
        foreach ($files as $name => $file)
        {
            // Skip directories (they would be added automatically)
            if (!$file->isDir())
            {
                // Get real and relative path for current file
                $filePath = $file->getRealPath();
                $relativePath = substr($filePath, strlen($rootPath) + 1);
         
                // Add current file to archive
                $zip->addFile($filePath, $relativePath);
            }
        }
         
        // Zip archive will be created only after closing object
        $zip->close();
        
        return TRUE;
    } else {
        return FALSE;
    }


}

Llama al método zip

$source = $source_directory;
$destination = $destination_directory;
$zipcreation = $this->zip_creation($source, $destination);
Mamun Sabuj
fuente
0

Hice alguna pequeña mejora en el guión.

  <?php
    $directory = "./";
    //create zip object
    $zip = new ZipArchive();
    $zip_name = time().".zip";
    $zip->open($zip_name,  ZipArchive::CREATE);
    $files = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($directory),
        RecursiveIteratorIterator::LEAVES_ONLY
    );
    foreach ($files as $file) {
        $path = $file->getRealPath();
        //check file permission
        if(fileperms($path)!="16895"){
            $zip->addFromString(basename($path),  file_get_contents($path)) ;
            echo "<span style='color:green;'>{$path} is added to zip file.<br /></span> " ;
        }
        else{
            echo"<span style='color:red;'>{$path} location could not be added to zip<br /></span>";
        }
    }
    $zip->close();
    ?>
Poohspear
fuente
Esto comprime los archivos, pero la lista del directorio se ha ido, ya no tiene directorio
Sujay sreedhar
0

Esto resolverá tu problema. Por favor pruebalo.

$zip = new ZipArchive;
$zip->open('testPDFZip.zip', ZipArchive::CREATE);
foreach (glob(APPLICATION_PATH."pages/recruitment/uploads/test_pdf_folder/*") as $file) {
    $new_filename = end(explode("/",$file));
    $zip->addFile($file,"emp/".$new_filename);
}           
$zip->close();
Lakhan
fuente
0

Para cualquiera que lea esta publicación y busque un por qué comprimir los archivos usando addFile en lugar de addFromString, eso no comprime los archivos con su ruta absoluta (simplemente comprime los archivos y nada más), vea mi pregunta y respuesta aquí

Skytiger
fuente