首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 壓縮與解壓縮完整實戰:ZipArchive、Tar、PharData 與第三方庫

     編輯:PHP 相關知識

PHP 內建壓縮擴展概覽

擴展/類別支援格式特點適用場景
ZipArchiveZIP原生、效能好、功能完整最常用、跨平台相容
PharDataTAR、ZIP、PHAR統一介面、支援串流、可建立 PHAR需要多格式支援、打包部署
Zlib (gz*)GZIP單檔壓縮、串流處理日誌壓縮、HTTP 壓縮傳輸
Bz2BZIP2壓縮率高、速度較慢大檔案長期存儲
Rar (PECL)RAR需額外安裝、授權限制解壓 RAR 封存

ZipArchive 完整實戰 (推薦首選)

建立壓縮檔:目錄遞歸壓縮

<?php
function zipDirectory(string $sourceDir, string $outputZip, array $options = []): bool {
    $defaults = [
        'includeHidden' => false,
        'followSymlinks' => false,
        'compression' => ZipArchive::CM_DEFAULT,
        'password' => null,
        'encryption' => ZipArchive::EM_AES_256,
    ];
    $opts = array_merge($defaults, $options);
    
    if (!is_dir($sourceDir)) {
        throw new InvalidArgumentException("來源目錄不存在: $sourceDir");
    }
    
    $zip = new ZipArchive();
    $flags = $opts['password'] ? ZipArchive::CREATE | ZipArchive::OVERWRITE : ZipArchive::CREATE;
    
    if ($zip->open($outputZip, $flags) !== true) {
        throw new RuntimeException("無法建立 ZIP: $outputZip");
    }
    
    if ($opts['password']) {
        $zip->setPassword($opts['password']);
        $zip->setEncryptionName('', $opts['encryption']);
    }
    
    $sourceDir = rtrim($sourceDir, DIRECTORY_SEPARATOR);
    $baseLen = strlen(dirname($sourceDir)) + 1;
    
    $iterator = new RecursiveIteratorIterator(
        new RecursiveDirectoryIterator($sourceDir, FilesystemIterator::SKIP_DOTS),
        RecursiveIteratorIterator::SELF_FIRST
    );
    
    foreach ($iterator as $file) {
        $path = $file->getPathname();
        $relativePath = substr($path, $baseLen);
        
        if (!$opts['includeHidden'] && preg_match('/(^|\/)\./', $relativePath)) {
            continue;
        }
        if (!$opts['followSymlinks'] && $file->isLink()) {
            continue;
        }
        
        if ($file->isDir()) {
            $zip->addEmptyDir($relativePath . '/');
        } else {
            $zip->addFile($path, $relativePath);
            $zip->setCompressionName($relativePath, $opts['compression']);
        }
    }
    
    return $zip->close();
}

// 使用範例
try {
    zipDirectory('/var/www/html/app', '/backups/app-' . date('Ymd-His') . '.zip', [
        'password' => 'strong_password',
        'compression' => ZipArchive::CM_BZIP2
    ]);
    echo "壓縮完成";
} catch (Exception $e) {
    echo "失敗: " . $e->getMessage();
}
?>

解壓縮與檔案列表

<?php
function extractZip(string $zipPath, string $destDir, ?string $password = null): bool {
    $zip = new ZipArchive();
    $flags = $password ? ZipArchive::RDONLY : 0;
    
    if ($zip->open($zipPath, $flags) !== true) {
        throw new RuntimeException("無法開啟 ZIP: $zipPath");
    }
    
    if ($password) {
        $zip->setPassword($password);
    }
    
    // 列出內容
    echo "壓縮檔包含 {$zip->count()} 個項目:\n";
    for ($i = 0; $i < $zip->count(); $i++) {
        $stat = $zip->statIndex($i);
        printf("  %s %10d %s\n", 
            $stat['name'], 
            $stat['size'], 
            date('Y-m-d H:i:s', $stat['mtime'])
        );
    }
    
    // 解壓 (可指定特定檔案)
    $result = $zip->extractTo($destDir);
    $zip->close();
    
    return $result;
}

// 串流讀取單一檔案 (不解壓整包)
function readZipEntry(string $zipPath, string $entryName, ?string $password = null): ?string {
    $zip = new ZipArchive();
    if ($zip->open($zipPath) !== true) return null;
    if ($password) $zip->setPassword($password);
    
    $content = $zip->getFromName($entryName);
    $zip->close();
    return $content;
}
?>

PharData:統一處理 TAR/ZIP/PHAR

<?php
// 建立 TAR.GZ
$phar = new PharData('project.tar.gz');
$phar->buildFromDirectory('/path/to/source');
$phar->compress(Phar::GZ); // 或 Phar::BZ2
$phar->delete(); // 刪除中間檔

// 解壓 TAR.GZ
$phar = new PharData('project.tar.gz');
$phar->decompress(); // 產生 project.tar
$phar = new PharData('project.tar');
$phar->extractTo('/destination');

// 建立 PHAR 部署包 (需 php.ini phar.readonly=0)
$phar = new Phar('myapp.phar');
$phar->buildFromDirectory('/path/to/src');
$phar->setStub($phar->createDefaultStub('index.php'));
$phar->compressFiles(Phar::GZ);
?>

串流壓縮:大檔案/記憶體受限環境

<?php
// GZIP 串流壓縮 (不載入整個檔案到記憶體)
function gzipStream(string $sourcePath, string $destPath): void {
    $in = fopen($sourcePath, 'rb');
    $out = gzopen($destPath, 'wb9'); // 9 = 最高壓縮
    
    while (!feof($in)) {
        gzwrite($out, fread($in, 8192));
    }
    
    fclose($in);
    gzclose($out);
}

// HTTP 回應直接輸出 GZIP
header('Content-Encoding: gzip');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="export.csv.gz"');

$out = gzopen('php://output', 'wb');
foreach ($largeDataset as $row) {
    gzwrite($out, implode(',', $row) . "\n");
}
gzclose($out);
?>

第三方庫推薦:更豐富功能

ZipStream-PHP 範例:大量檔案即時打包下載

<?php
require 'vendor/autoload.php';

use ZipStream\ZipStream;
use ZipStream\Option\Archive as ArchiveOptions;

$options = new ArchiveOptions();
$options->setSendHttpHeaders(true)
        ->setContentType('application/zip')
        ->setFilename('export-' . date('Ymd-His') . '.zip');

$zip = new ZipStream($options);

// 從資料庫/雲端逐筆新增,不佔用記憶體
foreach ($hugeFileList as $file) {
    $zip->addFileFromPath($file['local_path'], $file['archive_name']);
}

$zip->finish();
?>

效能與安全建議

  • 大檔案用串流:避免 memory_limit 耗盡
  • 密碼保護用 AES-256:ZipArchive::EM_AES_256,舊版 ZipCrypto 弱
  • 驗證解壓路徑:防止 Zip Slip 攻擊 (路徑遍歷 ../../etc/passwd)
  • 清理暫存檔:使用 sys_get_temp_dir() + register_shutdown_function
  • 備份驗證:壓縮後抽樣解壓驗證完整性

Zip Slip 防禦範例

<?php
function safeExtract(ZipArchive $zip, string $destDir): void {
    $destDir = realpath($destDir);
    
    for ($i = 0; $i < $zip->count(); $i++) {
        $entry = $zip->getNameIndex($i);
        $targetPath = $destDir . DIRECTORY_SEPARATOR . $entry;
        $targetPath = realpath($targetPath) ?: $targetPath; // 解析 symlink
        
        // 關鍵:確保目標路徑在目的目錄內
        if (strpos($targetPath, $destDir) !== 0) {
            throw new RuntimeException("Zip Slip 攻擊偵測: $entry");
        }
        
        $zip->extractTo($destDir, [$entry]);
    }
}
?>

PHP 內建擴展已足應付多數壓縮需求,配合串流處理與第三方庫,能優雅解決從小檔案打包到 TB 級資料匯出的各種場景。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
庫特點Composer
maennchen/ZipStream-PHP串流式 ZIP 下載、不佔記憶體、支援大檔案composer require maennchen/zipstream-php
chumper/zipperLaravel 風格鏈式 API、簡潔易用composer require chumper/zipper
stechstudio/laravel-zipstreamLaravel 整合、回應宏composer require stechstudio/laravel-zipstream