120 votes

Comment zipper [récursivement] un répertoire en PHP ?

Le répertoire est quelque chose comme :

home/
    file1.html
    file2.html
Another_Dir/
    file8.html
    Sub_Dir/
        file19.html

J'utilise la même classe PHP Zip que celle utilisée dans PHPMyAdmin. http://trac.seagullproject.org/browser/branches/0.6-bugfix/lib/other/Zip.php . Je ne suis pas sûr de savoir comment zipper un répertoire plutôt qu'un simple fichier. Voici ce que j'ai jusqu'à présent :

$aFiles = $this->da->getDirTree($target);
/* $aFiles is something like, path => filetime
Array
(
    [home] => 
    [home/file1.html] => 1251280379
    [home/file2.html] => 1251280377
    etc...
)

*/
$zip = & new Zip();
foreach( $aFiles as $fileLocation => $time ){
    $file = $target . "/" . $fileLocation;
    if ( is_file($file) ){
        $buffer = file_get_contents($file);
        $zip->addFile($buffer, $fileLocation);
    }
}
THEN_SOME_PHP_CLASS::toDownloadData($zip); // this bit works ok

mais lorsque j'essaie de décompresser le fichier zip téléchargé correspondant, j'obtiens "opération non autorisée".

Cette erreur se produit uniquement lorsque j'essaie de décompresser le fichier sur mon Mac. Lorsque je décompose le fichier en ligne de commande, le fichier se décompose correctement. Dois-je envoyer un type de contenu spécifique lors du téléchargement, actuellement 'application/zip' ?

0 votes

Ce code fonctionne réellement - mais pour une raison quelconque, vous ne pouvez pas le décompresser sous Mac OS (à moins d'utiliser CLI unzip). Le fichier Zip se décompose correctement sur PC.

0 votes

264voto

Alix Axel Points 63455

Voici une fonction simple qui peut compresser n'importe quel fichier ou répertoire de manière récursive, il suffit que l'extension zip soit chargée.

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 . '/'));
            }
            else if (is_file($file) === true)
            {
                $zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
            }
        }
    }
    else if (is_file($source) === true)
    {
        $zip->addFromString(basename($source), file_get_contents($source));
    }

    return $zip->close();
}

Appelez ça comme ça :

Zip('/folder/to/compress/', './compressed.zip');

1 votes

Zip('/folder/to/compress/', './compressed.zip'); Où dois-je mettre ça ?

5 votes

Cela a très bien fonctionné, ma seule question est que mon script s'exécute à partir d'un emplacement différent de celui des fichiers à zipper, donc lorsque je fournis le 1er argument, l'emplacement du chemin de fichier complet est utilisé dans le zip, comme ceci : C:\wamp\www\export\pkg -1211.191011 \pkg -1211.191011.zip, cette structure complète de dossiers imbriqués se trouvant dans la nouvelle archive. Existe-t-il un moyen d'adapter le script ci-dessus pour qu'il ne contienne que les fichiers et répertoires sur lesquels je pointe, et non le chemin complet dont ils proviennent ?

0 votes

@Danjah : J'ai porté cela depuis mon framework ( github.com/alixaxel/phunction ) mais l'a seulement adapté aux systèmes *nix, le problème que vous mentionnez n'existe pas dans le framework car les chemins sont normalisés. Essayez de remplacer toutes les barres obliques ( / ) avec deux barres obliques inversées (``). \\ `) - cela devrait résoudre votre problème (sous Windows) je pense.

21voto

Giorgio Barchiesi Points 2023

Encore un autre archivage récursif d'arbre de répertoire, implémenté comme une extension de ZipArchive. En bonus, une fonction d'aide à la compression d'arbre à un seul état est incluse. Le nom local optionnel est supporté, comme dans les autres fonctions de ZipArchive. Code de gestion des erreurs à ajouter...

class ExtendedZip extends ZipArchive {

    // Member function to add a whole file system subtree to the archive
    public function addTree($dirname, $localname = '') {
        if ($localname)
            $this->addEmptyDir($localname);
        $this->_addTree($dirname, $localname);
    }

    // Internal function, to recurse
    protected function _addTree($dirname, $localname) {
        $dir = opendir($dirname);
        while ($filename = readdir($dir)) {
            // Discard . and ..
            if ($filename == '.' || $filename == '..')
                continue;

            // Proceed according to type
            $path = $dirname . '/' . $filename;
            $localpath = $localname ? ($localname . '/' . $filename) : $filename;
            if (is_dir($path)) {
                // Directory: add & recurse
                $this->addEmptyDir($localpath);
                $this->_addTree($path, $localpath);
            }
            else if (is_file($path)) {
                // File: just add
                $this->addFile($path, $localpath);
            }
        }
        closedir($dir);
    }

    // Helper function
    public static function zipTree($dirname, $zipFilename, $flags = 0, $localname = '') {
        $zip = new self();
        $zip->open($zipFilename, $flags);
        $zip->addTree($dirname, $localname);
        $zip->close();
    }
}

// Example
ExtendedZip::zipTree('/foo/bar', '/tmp/archive.zip', ZipArchive::CREATE);

0 votes

Belle réponse Giorgio ! Elle donne de meilleurs résultats que Zip() sous Windows pour les structures arborescentes. Merci.

12voto

user2019515 Points 2166

J'ai édité Alix Axel de prendre un troisième argrument, en fixant ce troisième argrument à true tous les fichiers seront ajoutés sous le répertoire principal plutôt que directement dans le dossier zip.

Si le fichier zip existe, le fichier sera également supprimé.

Exemple :

Zip('/path/to/maindirectory','/path/to/compressed.zip',true);

Troisième argument true structure du zip :

maindirectory
--- file 1
--- file 2
--- subdirectory 1
------ file 3
------ file 4
--- subdirectory 2
------ file 5
------ file 6

Troisième argument false ou une structure zip manquante :

file 1
file 2
subdirectory 1
--- file 3
--- file 4
subdirectory 2
--- file 5
--- file 6

Code d'édition :

function Zip($source, $destination, $include_dir = false)
{

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

    if (file_exists($destination)) {
        unlink ($destination);
    }

    $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);

        if ($include_dir) {

            $arr = explode("/",$source);
            $maindir = $arr[count($arr)- 1];

            $source = "";
            for ($i=0; $i < count($arr) - 1; $i++) { 
                $source .= '/' . $arr[$i];
            }

            $source = substr($source, 1);

            $zip->addEmptyDir($maindir);

        }

        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 . '/'));
            }
            else if (is_file($file) === true)
            {
                $zip->addFromString(str_replace($source . '/', '', $file), file_get_contents($file));
            }
        }
    }
    else if (is_file($source) === true)
    {
        $zip->addFromString(basename($source), file_get_contents($source));
    }

    return $zip->close();
}

0 votes

Merci ! J'avais besoin d'inclure le répertoire principal dans ma situation.

0 votes

Votre fonction ne fonctionne pas, seul le répertoire principal (Root) est ajouté et rien n'est ajouté.

0 votes

Je sais qu'on a répondu à cette question il y a longtemps. Est-il possible d'avoir un nom personnalisé pour "maindirectory" au lieu du nom original ?

4voto

tazo todua Points 495

UTILISATION : cefile.php?dir=./path/to/folder (Après la fermeture éclair, il commence télécharger aussi :)

<?php
$exclude_some_files=
array(
        'mainfolder/folder1/filename.php',
        'mainfolder/folder5/otherfile.php'
);

//***************built from https://gist.github.com/ninadsp/6098467 ******
class ModifiedFlxZipArchive extends ZipArchive {
    public function addDirDoo($location, $name , $prohib_filenames=false) {
        if (!file_exists($location)) {  die("maybe file/folder path incorrect");}

        $this->addEmptyDir($name);
        $name .= '/';
        $location.= '/';
        $dir = opendir ($location);   // Read all Files in Dir

        while ($file = readdir($dir)){
            if ($file == '.' || $file == '..') continue;
            if (!in_array($name.$file,$prohib_filenames)){
                if (filetype( $location . $file) == 'dir'){
                    $this->addDirDoo($location . $file, $name . $file,$prohib_filenames );
                }
                else {
                    $this->addFile($location . $file, $name . $file);
                }
            }
        }
    }

    public function downld($zip_name){
        ob_get_clean();
        header("Pragma: public");   header("Expires: 0");   header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
        header("Cache-Control: private", false);    header("Content-Type: application/zip");
        header("Content-Disposition: attachment; filename=" . basename($zip_name) . ";" );
        header("Content-Transfer-Encoding: binary");
        header("Content-Length: " . filesize($zip_name));
        readfile($zip_name);
    }
}

//set memory limits
set_time_limit(3000);
ini_set('max_execution_time', 3000);
ini_set('memory_limit','100M');
$new_zip_filename='down_zip_file_'.rand(1,1000000).'.zip';  
// Download action
if (isset($_GET['dir']))    {
    $za = new ModifiedFlxZipArchive;
    //create an archive
    if  ($za->open($new_zip_filename, ZipArchive::CREATE)) {
        $za->addDirDoo($_GET['dir'], basename($_GET['dir']), $exclude_some_files); $za->close();
    }else {die('cantttt');}

if (isset($_GET['dir']))    {
    $za = new ModifiedFlxZipArchive;
    //create an archive
    if  ($za->open($new_zip_filename, ZipArchive::CREATE)) {
        $za->addDirDoo($_GET['dir'], basename($_GET['dir']), $exclude_some_files); $za->close();
    }else {die('cantttt');}

    //download archive
    //on the same execution,this made problems in some hostings, so better redirect
    //$za -> downld($new_zip_filename);
    header("location:?fildown=".$new_zip_filename); exit;
}   
if (isset($_GET['fildown'])){
    $za = new ModifiedFlxZipArchive;
    $za -> downld($_GET['fildown']);
}
?>

2voto

Phill Pafford Points 32046

Essayez ce lien <-- PLUS DE CODE SOURCE ICI

/** Include the Pear Library for Zip */
include ('Archive/Zip.php');

/** Create a Zipping Object...
* Name of zip file to be created..
* You can specify the path too */
$obj = new Archive_Zip('test.zip');
/**
* create a file array of Files to be Added in Zip
*/
$files = array('black.gif',
'blue.gif',
);

/**
* creating zip file..if success do something else do something...
* if Error in file creation ..it is either due to permission problem (Solution: give 777 to that folder)
* Or Corruption of File Problem..
*/

if ($obj->create($files)) {
// echo 'Created successfully!';
} else {
//echo 'Error in file creation';
}

?>; // We'll be outputting a ZIP
header('Content-type: application/zip');

// It will be called test.zip
header('Content-Disposition: attachment; filename="test.zip"');

//read a file and send
readfile('test.zip');
?>;

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X