web-dev-qa-db-fra.com

Comment [récursivement] compresser 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 le même PHP La classe ZIP 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 compresser 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, le message "opération non autorisée" s'affiche.

Cette erreur ne se produit que lorsque j'essaie de décompresser sur mon Mac, lorsque je décompresse le fichier en ligne de commande, le fichier est bien décompressé. Dois-je envoyer un type de contenu spécifique lors du téléchargement, actuellement "application/Zip"?

117
ed209

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');
249
Alix Axel

Encore un autre archivage récursif d'arborescence de répertoires, implémenté comme une extension de ZipArchive. En prime, une fonction d'assistance à la compression d'arborescence à une seule instruction est incluse. Le nom local facultatif est pris en charge, comme dans d'autres fonctions ZipArchive. Erreur de gestion du code à 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);
15
Giorgio Barchiesi

J'ai modifié la réponse de Alix Axel à prendre un troisième argument. Lorsque vous définissez ce troisième argument sur true, tous les fichiers seront ajoutés dans le répertoire principal plutôt que directement dans le zip. dossier.

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 Zip:

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

Troisième argument false ou structure Zip manquante:

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

Code édité:

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();
}
10
user2019515

UTILISATION: thisfile.php? Dir =./Chemin/vers/dossier (Après avoir compressé, il démarre 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']);
}
?>
4
T.Todua

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');
?>;
2
Phill Pafford

Voici mon code pour Zip les dossiers et ses sous-dossiers et ses fichiers et le rendre téléchargeable au format Zip

function Zip()
 {
$source='path/folder'// Path To the folder;
$destination='path/folder/abc.Zip'// Path to the file and file name ; 
$include_dir = false;
$archive = 'abc.Zip'// File Name ;

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

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

$Zip = new ZipArchive;

if (!$Zip->open($archive, 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));
}
$Zip->close();

header('Content-Type: application/Zip');
header('Content-disposition: attachment; filename='.$archive);
header('Content-Length: '.filesize($archive));
readfile($archive);
unlink($archive);
}

Si un problème avec le code, faites le moi savoir.

1
nitin

Ce code fonctionne pour Windows et Linux.

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

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

if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
    DEFINE('DS', DIRECTORY_SEPARATOR); //for windows
} else {
    DEFINE('DS', '/'); //for linux
}


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

if (is_dir($source) === true)
{
    $files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);
    echo $source;
    foreach ($files as $file)
    {
        $file = str_replace('\\',DS, $file);
        // Ignore "." and ".." folders
        if( in_array(substr($file, strrpos($file, DS)+1), array('.', '..')) )
            continue;

        $file = realpath($file);

        if (is_dir($file) === true)
        {
            $Zip->addEmptyDir(str_replace($source . DS, '', $file . DS));
        }
        else if (is_file($file) === true)
        {
            $Zip->addFromString(str_replace($source . DS, '', $file), file_get_contents($file));
        }
        echo $source;
    }
}
else if (is_file($source) === true)
{
    $Zip->addFromString(basename($source), file_get_contents($source));
}

return $Zip->close();
}
0
Ammar Qala

Voici la fonction simple, facile à lire, récursive qui fonctionne très bien:

function Zip_r($from, $Zip, $base=false) {
    if (!file_exists($from) OR !extension_loaded('Zip')) {return false;}
    if (!$base) {$base = $from;}
    $base = trim($base, '/');
    $Zip->addEmptyDir($base);
    $dir = opendir($from);
    while (false !== ($file = readdir($dir))) {
        if ($file == '.' OR $file == '..') {continue;}

        if (is_dir($from . '/' . $file)) {
            Zip_r($from . '/' . $file, $Zip, $base . '/' . $file);
        } else {
            $Zip->addFile($from . '/' . $file, $base . '/' . $file);
        }
    }
    return $Zip;
}
$from = "/path/to/folder";
$base = "basezipfolder";
$Zip = new ZipArchive();
$Zip->open('zipfile.Zip', ZIPARCHIVE::CREATE);
$Zip = Zip_r($from, $Zip, $base);
$Zip->close();
0
Tarik

Voici ma version de base sur celle d'Alix, fonctionne sous Windows et, espérons-le aussi, sur * nix:

function addFolderToZip($source, $destination, $flags = ZIPARCHIVE::OVERWRITE)
{
    $source = realpath($source);
    $destination = realpath($destination);

    if (!file_exists($source)) {
        die("file does not exist: " . $source);
    }

    $Zip = new ZipArchive();
    if (!$Zip->open($destination, $flags )) {
        die("Cannot open Zip archive: " . $destination);
    }

    $files = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($source), RecursiveIteratorIterator::SELF_FIRST);

    $sourceWithSeparator = $source . DIRECTORY_SEPARATOR;
    foreach ($files as $file)
    {
        // Ignore "." and ".." folders
        if(in_array(substr($file,strrpos($file, DIRECTORY_SEPARATOR)+1),array('.', '..')))
            continue;

        if (is_dir($file) === true)
        {
            $Zip->addEmptyDir(
                str_replace($sourceWithSeparator, '', $file . DIRECTORY_SEPARATOR));
        }
        else if (is_file($file) === true)
        {
            $Zip->addFile($file, str_replace($sourceWithSeparator, '', $file));
        }
    }

    return $Zip->close();
}
0
Ohad Schneider

Excellente solution, mais pour Windows, il me faut apporter des modifications. En dessous du code de modification

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;

        if (is_dir($file) === true)
        {
            $Zip->addEmptyDir(str_replace($source . '/', '', $file));
        }
        else if (is_file($file) === true)
        {

            $str1 = str_replace($source . '/', '', '/'.$file);
            $Zip->addFromString($str1, file_get_contents($file));

        }
    }
}
else if (is_file($source) === true)
{
    $Zip->addFromString(basename($source), file_get_contents($source));
}

return $Zip->close();
}
0
Juan

J'avais besoin d'exécuter cette fonction Zip sous Mac OSX

donc je voudrais toujours compresser ce .DS_Store.

Je me suis adapté https://stackoverflow.com/users/2019515/user2019515 en incluant des fichiers additionalIgnore.

function zipIt($source, $destination, $include_dir = false, $additionalIgnoreFiles = array())
{
    // Ignore "." and ".." folders by default
    $defaultIgnoreFiles = array('.', '..');

    // include more files to ignore
    $ignoreFiles = array_merge($defaultIgnoreFiles, $additionalIgnoreFiles);

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

            // purposely ignore files that are irrelevant
            if( in_array(substr($file, strrpos($file, '/')+1), $ignoreFiles) )
                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();
}

SO pour ignorer le .DS_Store de Zip, vous exécutez

zipIt ('/ path/to/folder', '/path/to/compressed.Zip', false, array ('.DS_Store'));

0
Kim Stacks