ZipArchive 是 PHP 內建處理 ZIP 檔案的類別,支援建立新壓縮檔、讀取現有檔案、列出內容、解壓縮、新增/刪除/重命名檔案、設定註解、甚至加密(PHP 7.2+)。本文提供完整操作手冊,涵蓋所有常見場景與錯誤處理最佳實踐。
| 常數 | 說明 |
|---|---|
ZipArchive::CREATE | 建立新檔,若存在則覆蓋 |
ZipArchive::OVERWRITE | 覆蓋現有檔(等同 CREATE) |
ZipArchive::EXCL | 獨佔建立,檔案存在則失敗 |
ZipArchive::CHECKCONS | 檢查一致性,失敗則不開啟 |
ZipArchive::RDONLY | 唯讀模式開啟 |
<?php
$zip = new ZipArchive();
$filename = 'backup_' . date('Ymd_His') . '.zip';
if ($zip->open($filename, ZipArchive::CREATE) !== true) {
die("無法建立 ZIP: $filename");
}
// 新增單一檔案(第二參數為 ZIP 內路徑)
$zip->addFile('/var/www/html/index.php', 'public/index.php');
$zip->addFile('/var/www/html/.env', 'config/.env'); // 重新命名路徑
// 新增字串內容為檔案
$zip->addFromString('README.txt', "專案備份\n建立時間: " . date('c'));
// 遞歸新增整個目錄
function addDirRecursive(ZipArchive $zip, string $realPath, string $zipPath): void
{
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($realPath, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::SELF_FIRST
);
foreach ($iterator as $item) {
$relativePath = $zipPath . '/' . $iterator->getSubPathName();
if ($item->isDir()) {
$zip->addEmptyDir($relativePath);
} else {
$zip->addFile($item->getPathname(), $relativePath);
}
}
}
addDirRecursive($zip, '/var/www/html/storage', 'storage');
$zip->close();
echo "ZIP 建立完成: $filename";
?>
<?php
$zip = new ZipArchive();
if ($zip->open('backup.zip') !== true) die("開啟失敗");
echo "檔案總數: {$zip->count()}\n";
echo "狀態: {$zip->status}\n";
echo "註解: " . ($zip->getComment() ?: '(無)') . "\n";
for ($i = 0; $i < $zip->count(); $i++) {
$stat = $zip->statIndex($i);
echo sprintf(
"[%d] %-50s %10d bytes 壓縮後: %10d CRC32: %08X 時間: %s\n",
$i,
$stat['name'],
$stat['size'],
$stat['comp_size'],
$stat['crc'],
date('Y-m-d H:i:s', $stat['mtime'])
);
}
$zip->close();
?>
<?php
$zip = new ZipArchive();
if ($zip->open('backup.zip') !== true) die("開啟失敗");
// 提取全部到目錄
$zip->extractTo('/restore/path');
// 或提取特定檔案
$zip->extractTo('/restore/path', ['public/index.php', 'config/.env']);
// 提取單一檔案到字串(不寫入磁碟)
$content = $zip->getFromName('README.txt');
if ($content !== false) {
echo "README 內容:\n$content";
}
$zip->close();
?>
<?php
$zip = new ZipArchive();
// 必須用 OVERWRITE 或 CREATE 模式開啟才能修改
if ($zip->open('backup.zip', ZipArchive::OVERWRITE) !== true) die("開啟失敗");
// 刪除單一檔案
$zip->deleteName('config/.env');
// 刪除目錄(需先刪除裡面所有檔案,或遞歸刪除)
// ZipArchive 無直接刪除目錄方法,需遍歷刪除
$prefix = 'storage/';
for ($i = $zip->count() - 1; $i >= 0; $i--) {
$name = $zip->getNameIndex($i);
if (str_starts_with($name, $prefix)) {
$zip->deleteIndex($i);
}
}
$zip->close();
echo "刪除完成";
?>
<?php
$zip = new ZipArchive();
if ($zip->open('backup.zip', ZipArchive::OVERWRITE) !== true) die("開啟失敗");
// 重命名:讀取內容 → 新增新名稱 → 刪除舊名稱
$oldName = 'public/index.php';
$newName = 'public/index_v2.php';
$content = $zip->getFromName($oldName);
if ($content !== false) {
$zip->addFromString($newName, $content);
$zip->deleteName($oldName);
echo "重命名完成: $oldName -> $newName";
}
// 更新檔案內容
$zip->addFromString('README.txt', "更新後的說明\n" . date('c'));
$zip->close();
?>
<?php
$zip = new ZipArchive();
if ($zip->open('secret.zip', ZipArchive::CREATE) !== true) die("建立失敗");
$zip->addFromString('secret.txt', '這是機密資料');
// 設定加密密碼(支援 AES-256,需 libzip 1.6+)
$zip->setPassword('StrongP@ssw0rd!');
$zip->setEncryptionName('secret.txt', ZipArchive::EM_AES_256);
$zip->close();
// 解壓縮時需提供密碼
$zip2 = new ZipArchive();
if ($zip2->open('secret.zip') === true) {
$zip2->setPassword('StrongP@ssw0rd!');
$zip2->extractTo('/decrypted');
$zip2->close();
}
?>
<?php
$zip = new ZipArchive();
// 開啟虛擬記憶體檔案(PHP 8.0+ 支援 "php://memory")
if ($zip->open('php://memory', ZipArchive::CREATE) !== true) die("失敗");
$zip->addFromString('data.json', json_encode(['users' => 123, 'time' => time()]));
$zip->addFromString('log.txt', "系統日誌...\n");
$zip->close();
// 取得二進位內容
$zipData = file_get_contents('php://memory'); // 注意:close 後可能無法讀取
// 正確做法:open 時用 variable:// 參考(PHP 8.2+)
// 或改用 tempnam() 暫存檔
// 直接輸出下載
header('Content-Type: application/zip');
header('Content-Disposition: attachment; filename="export.zip"');
echo $zipData;
?>
| 錯誤 | 原因與解決 |
|---|---|
open() 回傳整數錯誤碼 | 非 true 即失敗。常見碼:9=找不到檔, 10=非 ZIP, 11=記憶體不足, 14=權限不足。用 ZipArchive::getStatusString() (PHP 8.0+) 取得訊息。 |
| 中文檔名亂碼 | ZIP 標準早期不支援 UTF-8。確保 PHP 7.2+、libzip 1.6+,檔名會自動用 UTF-8 標記。舊系統解壓需指定編碼(如 `unzip -O cp950`)。 |
| 大檔案記憶體耗盡 | 避免 getFromName() 讀取巨大檔案到記憶體。改用 extractTo() 直接寫入磁碟。 |
| 無法刪除目錄 | ZipArchive 無遞歸刪除目錄 API,必須先刪除所有子檔案(由後往前遍歷索引)。 |
| 加密不支援 | 需 PHP 7.2+ 且編譯時啟用 --with-libzip(系統 libzip ≥ 1.2)。共享主機常不支援,請確認 phpinfo() 的 zip 區塊。 |
$zip->setCompressionName($name, ZipArchive::CM_STORE) 加速,或平行處理(多進程各壓縮部分再合併)。ZipStream 套件(composer require maennchen/zipstream-php)實現「邊產生邊下載」,零記憶體佔用。close()。ZipArchive 功能強大且無外部相依,是 PHP 專案處理封裝、備份、匯出、下載的首選工具。