Como zipar uma pasta de arquivos com PHP

Ontem eu precisei zipar alguns arquivos, ai pensei: hummmm por que não criar uma classe para fazer isso? então bora lá criar uma classe em PHP que permite zipar uma pasta de arquivos de forma eficiente e prática. Essa classe pode ser útil para quem precisa compactar arquivos para enviar por e-mail, fazer backup ou economizar espaço em disco.

A classe ClsZipper


use DirectoryIterator;
use InvalidArgumentException;
use RuntimeException;
use ZipArchive;

class ClsZipper {
    private $maxFilesPerZip;

    public function __construct($maxFilesPerZip = 100) {
        $this->maxFilesPerZip = $maxFilesPerZip;
    }

    public function zipFolder($path, $destFolder = null) {
        if (!is_dir($path)) {
            throw new InvalidArgumentException("$path não é um diretório válido");
        }

        if (!file_exists($destFolder) || !is_dir($destFolder)) {
            throw new InvalidArgumentException("$destFolder não é um diretório válido");
        }

        $zip = new ZipArchive();
        $zipName = basename($path) . ".zip";
        $zipPath = $destFolder . DIRECTORY_SEPARATOR . $zipName;

        if ($zip->open($zipPath, ZipArchive::CREATE) !== true) {
            throw new RuntimeException("Não foi possível criar o arquivo zipado: $zipPath");
        }

        $hashes = [];
        $hashFile = $destFolder . DIRECTORY_SEPARATOR . md5($path) . '.txt';

        if (file_exists($hashFile)) {
            $hashes = array_filter(array_map('trim', file($hashFile)));
        }

        $count = 0;

        foreach (new DirectoryIterator($path) as $file) {
            if (!$file->isDot() && $file->isFile()) {
                $hash = md5_file($file->getPathname());

                if (in_array($hash, $hashes)) {
                    continue;
                }

                $zip->addFile($file->getPathname(), $file->getFilename());
                $hashes[] = $hash;
                $count++;

                if ($count == $this->maxFilesPerZip) {
                    $zip->close();
                    $zipName = basename($path) . '-' . count(glob($destFolder . DIRECTORY_SEPARATOR . basename($path) . '-*.zip')) . '.zip';
                    $zipPath = $destFolder . DIRECTORY_SEPARATOR . $zipName;

                    if ($zip->open($zipPath, ZipArchive::CREATE) !== true) {
                        throw new RuntimeException("Não foi possível criar o arquivo zipado: $zipPath");
                    }

                    $count = 0;
                }
            }
        }

        $zip->close();

        if (!empty($hashes)) {
            file_put_contents($hashFile, implode(PHP_EOL, $hashes));
        }

        return "Arquivo zipado criado com sucesso: $zipName";
    }
}

Explicando o código

A classe ClsZipper tem um atributo privado chamado ‘$maxFilesPerZip’, que define o número máximo de arquivos que podem ser adicionados a um único arquivo zip. O valor padrão desse atributo é 100, mas pode ser alterado no construtor da classe.

O método ‘zipFolder’ recebe dois parâmetros: ‘$path’, que é o caminho da pasta que queremos zipar, e ‘$destFolder’, que é o caminho da pasta onde vamos salvar os arquivos zipados. Se esses parâmetros não forem válidos, o método lança uma exceção do tipo ‘InvalidArgumentException’.

O método usa a classe ‘ZipArchive’, que faz parte da extensão ‘zip’ do PHP, para criar e manipular os arquivos zipados. O nome do arquivo zip é formado pelo nome da pasta original mais a extensão ‘.zip’. O caminho completo do arquivo zip é formado pela concatenação do caminho do destino com o nome do arquivo zip.

O método tenta abrir o arquivo zip com a flag ‘ZipArchive::CREATE’, que indica que o arquivo deve ser criado se não existir. Se a abertura falhar, o método lança uma exceção do tipo ‘RuntimeException’.

O método também usa um array chamado ‘$hashes’, que armazena os hashes MD5 dos arquivos que já foram zipados. Esses hashes são salvos em um arquivo de texto na pasta de destino, cujo nome é formado pelo hash MD5 da pasta original mais a extensão ‘.txt’. Se esse arquivo existir, o método lê os hashes dele e os armazena no array.

O método usa um objeto da classe ‘DirectoryIterator’ para iterar sobre os arquivos da pasta que queremos zipar. Para cada arquivo, o método verifica se ele não é um ponto (que representa o diretório atual ou o pai) e se ele é um arquivo (e não uma subpasta).

O método calcula o hash MD5 do arquivo usando a função ‘md5_file’ e verifica se ele já está no array de hashes. Se estiver, significa que o arquivo já foi zipado antes e pode ser ignorado. Se não estiver, o método adiciona o arquivo ao zip usando o método ‘addFile’, que recebe o caminho completo do arquivo e o nome dele. O método também adiciona o hash do arquivo ao array de hashes e incrementa uma variável chamada ‘$count’, que conta quantos arquivos foram adicionados ao zip.

Se o ‘$count’ for igual ao ‘$maxFilesPerZip’, significa que o limite de arquivos por zip foi atingido e é preciso criar um novo arquivo zip. O método fecha o zip atual usando o método ‘close’ e gera um novo nome para o próximo zip, usando o nome da pasta original mais um hífen e um número sequencial, seguido da extensão ‘.zip’. O método tenta abrir o novo zip com a flag ‘ZipArchive::CREATE’ e lança uma exceção se falhar. O método também zera o ‘$count’ para começar a contar os arquivos do novo zip.

Depois de iterar sobre todos os arquivos da pasta, o método fecha o último zip criado e verifica se o array de hashes não está vazio. Se não estiver, significa que houve algum arquivo zipado e é preciso salvar os hashes no arquivo de texto na pasta de destino, usando a função ‘file_put_contents’.

Finalmente, o método retorna uma mensagem de sucesso com o nome do último arquivo zip criado.

Como usar a classe

<?php
require_once 'ClsZipper.php'; // inclui a definição da classe
$zipper = new ClsZipper(); // cria uma instância da classe
$result = $zipper->zipFolder('fotos', 'backup'); // chama o método para zipar a pasta
echo $result; // imprime o resultado

Esse código vai criar um ou mais arquivos zip na pasta ‘backup’, com os nomes ‘fotos.zip’, ‘fotos-1.zip’, ‘fotos-2.zip’ etc., dependendo do número de arquivos na pasta ‘fotos’. Também vai criar um arquivo de texto chamado ‘c4ca4238a0b923820dcc509a6f75849b.txt’, que é o hash MD5 da pasta ‘fotos’, contendo os hashes dos arquivos zipados.

Se quisermos alterar o número máximo de arquivos por zip, podemos passar um valor diferente no construtor da classe. Por exemplo, se quisermos limitar a 50 arquivos por zip, podemos fazer assim:

<?php
require_once 'ClsZipper.php'; // inclui a definição da classe
$zipper = new ClsZipper(50); // cria uma instância da classe com um valor diferente
$result = $zipper->zipFolder('fotos', 'backup'); // chama o método para zipar a pasta
echo $result; // imprime o resultado

Deixe um comentário