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

PHP 實現 ZIP 文件在線解壓功能

     編輯:PHP 相關知識

在無 Shell 訪問權限的虛擬主機環境中,使用 PHP 內建 ZipArchive 或舊版 zip_* 函數可實現壓縮包上傳後自動解壓,適用於模板安裝、插件更新、批量圖片部署等場景。本文提供兩種版本的完整代碼並說明權限與安全注意事項。

現代寫法:ZipArchive(PHP 5.2+,推薦)

<?php
/**
 * 解壓 ZIP 到指定目錄
 *
 * @param string $zipPath   壓縮包路徑
 * @param string $destDir   目標目錄(會自動建立)
 * @param array  $options   可選:['overwrite' => true, 'password' => 'secret']
 * @return array ['success' => bool, 'message' => string, 'files' => array]
 */
function unzip_archive($zipPath, $destDir, $options = []) {
    if (!file_exists($zipPath)) {
        return ['success' => false, 'message' => '壓縮包不存在'];
    }
    if (!is_dir($destDir)) {
        mkdir($destDir, 0755, true);
    }

    $zip = new ZipArchive();
    $res = $zip->open($zipPath);
    if ($res !== true) {
        return ['success' => false, 'message' => "無法打開壓縮包,錯誤碼: $res"];
    }

    // 密碼保護支援(PHP 7.2+)
    if (!empty($options['password'])) {
        if (!$zip->setPassword($options['password'])) {
            $zip->close();
            return ['success' => false, 'message' => '密碼錯誤或不支援加密格式'];
        }
    }

    $overwrite = $options['overwrite'] ?? true;
    $extracted = [];

    for ($i = 0; $i < $zip->numFiles; $i++) {
        $stat = $zip->statIndex($i);
        $filename = $stat['name'];

        // 安全過濾:防止目錄遍歷攻擊 (../../etc/passwd)
        $filename = ltrim($filename, '/\\');
        if (strpos($filename, '..') !== false) {
            continue; // 跳過危險路徑
        }

        $targetPath = $destDir . DIRECTORY_SEPARATOR . $filename;

        if ($stat['name'][-1] === '/') { // 目錄項
            if (!is_dir($targetPath)) {
                mkdir($targetPath, 0755, true);
            }
            continue;
        }

        // 確保父目錄存在
        $parentDir = dirname($targetPath);
        if (!is_dir($parentDir)) {
            mkdir($parentDir, 0755, true);
        }

        // 覆蓋策略
        if (file_exists($targetPath) && !$overwrite) {
            continue;
        }

        // 解壓單個文件
        $content = $zip->getFromIndex($i);
        if ($content === false) {
            continue;
        }
        file_put_contents($targetPath, $content);
        $extracted[] = $filename;
    }

    $zip->close();
    return ['success' => true, 'message' => '解壓完成', 'files' => $extracted];
}

// 使用範例
$result = unzip_archive('uploads/package.zip', 'public/themes/new-theme', [
    'overwrite' => true,
    // 'password' => 'secret123' // 若有加密
]);
var_dump($result);

舊版寫法:zip_* 函數(PHP 4/5 早期,僅作參考)

<?php
// 僅供維護舊專案參考,新專案請用 ZipArchive
$zip = zip_open("moooredale.zip");
if ($zip) {
    while ($zip_entry = zip_read($zip)) {
        $name = zip_entry_name($zip_entry);
        // 安全過濾同前
        $fp = fopen($name, "w");
        if (zip_entry_open($zip, $zip_entry, "r")) {
            $buf = zip_entry_read($zip_entry, zip_entry_filesize($zip_entry));
            fwrite($fp, $buf);
            zip_entry_close($zip_entry);
        }
        fclose($fp);
    }
    zip_close($zip);
}

關鍵安全與效能要點

  1. 路徑遍歷防護:必須過濾 ..、絕對路徑、符號鏈接,否則攻擊者可寫入任意文件(如 ../../../var/www/html/shell.php)。
  2. 文件類型驗證:解壓前用 mime_content_type() 或 finfo 確認為 application/zip,防止偽裝的惡意腳本。
  3. 大小限制:設定 upload_max_filesize、post_max_size、memory_limit,避免大包導致 OOM。
  4. 執行時間:大包解壓可能超過 max_execution_time,可用 set_time_limit(0) 或分塊異步處理。
  5. 權限控制:目標目錄需 Web 進程寫入權限(通常 0755),解壓後文件建議 0644,目錄 0755。
  6. 清理臨時文件:解壓完成後刪除上傳的 ZIP,釋放磁碟空間。

進階:異步佇列解壓大文件

<?php
// 控制器:接收上傳 -> 存入臨時目錄 -> 推入隊列 -> 立即返回
// 隊列任務:調用 unzip_archive() -> 記錄日誌 -> 通知前端(WebSocket/輪詢)
// 適用於 >100MB 或文件數 >10k 的場景

推薦使用 Laravel Queue、Symfony Messenger 或原生 Redis 列表實現異步解壓,避免阻塞 HTTP 請求導致超時或用戶體驗差。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved