Como compactar uma pasta inteira usando PHP

131

Encontrei aqui no stackoveflow alguns códigos sobre como ZIP um arquivo específico, mas e uma pasta específica?

Folder/
  index.html
  picture.jpg
  important.txt

dentro para dentro My Folder, existem arquivos. depois de fechar o zíper My Folder, eu também quero excluir todo o conteúdo da pasta, exceto important.txt.

Encontrei isso aqui na pilha

Preciso da tua ajuda. obrigado.

woninana
fonte
Tanto quanto posso ver, o link do stackoverflow que você forneceu realmente compacta vários arquivos. Com qual parte você tem problemas?
Lasse Espeholt
@lasseespeholt O link que te dei fecha apenas uma arquivo específico, não a pasta e o conteúdo da pasta ..
woninana
Ele pega uma matriz de arquivos (essencialmente uma pasta) e adiciona todos os arquivos ao arquivo zip (o loop). Eu posso ver que uma boa resposta foi postada agora +1 :) que é o mesmo código, a matriz é apenas uma lista de arquivos de um diretório agora.
Lasse Espeholt
possível duplicata de compressa pasta / arquivo usando o script php
Pekka

Respostas:

320

Código atualizado em 22/04/2015.

Compacte uma pasta inteira:

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

Compacte uma pasta inteira e exclua todos os arquivos, exceto "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
fonte
2
Você deve definir chmod (gravável) no diretório (onde este script está localizado) como 777. Por exemplo: Se o script estiver localizado em /var/www/localhost/script.php, será necessário definir o chmod 0777 em diretório / var / www / localhost /.
Dador 06/02
3
A exclusão dos arquivos antes da chamada $zip->close()não funcionará. Verifique minha resposta aqui
hek2mgl
10
@ alnassre que é requisito da pergunta: "Eu também quero excluir todo o conteúdo da pasta, exceto important.txt". Também aconselho você a sempre ler o código antes de executá-lo.
Dador 21/04
1
@alnassre hahahaha ... sorry :) ... hahaha
Ondrej Rafaj
1
@ nick-newman, sim, para calcular a porcentagem, você pode usar php.net/manual/ru/function.iterator-count.php + contador dentro do loop. Com relação ao nível de compactação - não é possível com o ZipArchive no momento: stackoverflow.com/questions/1833168/…
Dador
54

Existe um método não documentado útil na classe 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();

Agora documentado em: www.php.net/manual/en/ziparchive.addglob.php

Mark Baker
fonte
2
@netcoder - os benefícios de ter escrito o phpt para testá-lo ... basicamente, leia a fonte da classe ZipArchive e a encontrou lá ... também há um método addPattern () não documentado que usa um padrão de estilo regexp, mas nunca conseguiu que o trabalho (pode ser um bug na classe)
Mark Baker
1
@ kread - você pode usar isso com qualquer lista de arquivos que possa ser extraída usando glob (), então eu achei extremamente útil desde que o descobri.
6286 Mark Baker
@ MarkBaker Eu sei que esse comentário está chegando anos depois que você postou, só estou tentando a minha sorte aqui. Também postei uma pergunta aqui sobre como compactar. Estou prestes a experimentar o método glob que você postou aqui, mas meu principal problema é que não posso usar addFromString e uso addFile, que está falhando silenciosamente. Você tem alguma ideia do que pode estar errado ou do que eu posso estar fazendo de errado?
Skytiger
@ user1032531 - a última linha da minha postagem (editada em 13 de dezembro de 2013) indica exatamente isso, com um link para a página de documentos.
Mark Baker
6
É addGlobrecursivo?
Vincent Poirier
20

Tente o seguinte:

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

Isso não será compactado recursivamente.

codificador de rede
fonte
Com certeza apagar alguns arquivos em My folder, mas eu também tenho uma pasta dentro de uma pasta My folderque me dá um erro de: Permissão negada desvinculando a pasta com emMy folder
woninana
@ Stupefy: Tente em if (!is_dir($file) && $file != 'target_folder...')vez disso. Ou marque a opção @kread answer se quiser compactar recursivamente, é a maneira mais eficiente.
Netcoder
A pasta dentro do My folderarquivo ainda não foi excluída, mas não há mais erros de qualquer maneira.
Woninana 6/02
Também esqueci de mencionar que não existem arquivos .zip criados.
Woninana 6/02
1
A exclusão dos arquivos antes da chamada $zip->close()não funcionará. Verifique minha resposta aqui
hek2mgl
19

Presumo que isso esteja sendo executado em um servidor em que o aplicativo zip esteja no caminho de pesquisa. Deve ser verdade para todos os baseados em unix e acho que a maioria dos servidores baseados em janelas.

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

O archive residirá em archive.zip posteriormente. Lembre-se de que os espaços em branco nos nomes de arquivos ou pastas são uma causa comum de erros e devem ser evitados sempre que possível.

Kevin Read
fonte
15

Eu tentei com o código abaixo e está funcionando. O código é auto-explicativo. Entre em contato se tiver alguma dúvida.

<?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
fonte
Excelente solução. Também funciona no laravel 5.5. realmente gostei disso. (y)
Web Artisan
1
Ótimo código! Limpo, simples e funcionando perfeitamente! ;) Parece-me a melhor resposta. Se puder ajudar alguém: acabei de adicionar ini_set('memory_limit', '512M');antes da execução do script e ini_restore('memory_limit');no final. Era necessário evitar a falta de memória no caso de pastas pesadas (era uma pasta maior que 500 MB).
Jacopo Pace
1
No meu ambiente (PHP 7.3, Debian), um arquivo ZIP sem uma listagem de diretório foi criado (arquivo grande e vazio). Eu tive que mudar a seguinte linha: $ name. = '/'; em $ name = ($ name == '.'? '': $ name. '/');
Gerfried
Isso está funcionando para mim. Obrigado por compartilhar. Felicidades!
Sathiska
8

Esta é uma função que fecha uma pasta inteira e seu conteúdo em um arquivo zip e você pode usá-lo de maneira simples assim:

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

função:

// 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
fonte
Como incluir subpastas também no backup automaticamente com esse script? @Alireza
floCoder
2

Por que não tentar o script EFS PhP-ZiP MultiVolume ... Eu compactuei e transferi centenas de shows e milhões de arquivos ... ssh é necessário para criar arquivos efetivamente.

Mas acredito que os arquivos resultantes podem ser usados ​​com o exec diretamente do php:

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

Não sei se funciona Eu não tentei ...

"o segredo" é que o tempo de execução do arquivamento não deve exceder o tempo permitido para a execução do código PHP.

ByREV
fonte
1

Este é um exemplo prático de criação de ZIPs em 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();
Sun Love
fonte
1

Encontrei este post no google como o segundo melhor resultado, primeiro estava usando exec :(

De qualquer forma, embora isso não atendesse exatamente às minhas necessidades. Decidi postar uma resposta para outras pessoas com minha versão rápida, porém extensa.

RECURSOS DO SCRIPT

  • Nomeação de arquivos de backup dia a dia, PREFIX-AAAA-MM-DD-POSTFIX.EXTENSION
  • Relatório de arquivo / ausente
  • Listagem de backups anterior
  • Não compacta / inclui backups anteriores;)
  • Funciona no windows / linux

Enfim, no script .. Embora possa parecer muito .. Lembre-se de que há excesso aqui .. Portanto, sinta-se à vontade para excluir as seções de relatórios conforme necessário ...

Também pode parecer bagunçado também e certas coisas podem ser limpas facilmente ... Portanto, não comente, é apenas um script rápido com comentários básicos lançados .. NÃO PARA USO AO VIVO .. Mas fácil de limpar para uso ao vivo !

Neste exemplo, ele é executado a partir de um diretório que está dentro da pasta raiz www / public_html. Portanto, é necessário apenas subir uma pasta para chegar à raiz.

<?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];
    }
?>

O QUE ISSO FAZ??

Ele simplesmente compactará o conteúdo completo da variável $ pathBase e armazenará o zip na mesma pasta. Ele faz uma detecção simples para backups anteriores e os ignora.

BACKUP CRON

Este script eu acabei de testar no linux e funcionou bem em um trabalho cron com o uso de uma URL absoluta para o pathBase.

Angry 84
fonte
Eu também excluído o script de exclusão, você pode ver a resposta aceite para este
irritado 84
Tem que amar aqueles votos aleatórios sem um comentário explicando o porquê.
irritada 84
1

Use esta função:

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

Exemplo de uso:

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

Use isso está funcionando bem.

$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
fonte
1

Crie uma pasta zip no PHP.

Método de criação 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;
    }


}

Chame o método zip

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

Eu fiz uma pequena melhoria no script.

  <?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
fonte
Esta não zip os arquivos, mas a listagem de diretório se foi ele dose não tem diretório mais
Sujay sreedhar
0

Isso resolverá seu problema. Por favor tente.

$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
fonte
0

Para quem está lendo este post e procura um porquê compactar os arquivos usando addFile em vez de addFromString, que não compacta os arquivos com seu caminho absoluto (apenas fecha os arquivos e nada mais), veja minha pergunta e resposta aqui

Skytiger
fonte