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

PHP ZipArchive 解壓縮:線上解壓、目錄遍歷、編碼處理、安全防禦

     編輯:PHP 綜合知識

基礎解壓函數

function unzip_file(string $zipPath, string $destDir): bool {
    if (!file_exists($zipPath)) throw new Exception("ZIP 不存在: $zipPath");
    if (!is_dir($destDir)) mkdir($destDir, 0755, true);
    
    $zip = new ZipArchive();
    $res = $zip->open($zipPath);
    if ($res !== true) throw new Exception("無法打開 ZIP: 錯誤碼 $res");
    
    // 安全:檢查路徑遍歷
    for ($i = 0; $i < $zip->numFiles; $i++) {
        $name = $zip->getNameIndex($i);
        if (strpos($name, '..') !== false || $name[0] === '/') {
            $zip->close();
            throw new Exception("檢測到路徑遍歷攻擊: $name");
        }
    }
    
    $ok = $zip->extractTo($destDir);
    $zip->close();
    return $ok;
}

中文檔名編碼處理

function unzip_chinese(string $zipPath, string $destDir): bool {
    $zip = new ZipArchive();
    $zip->open($zipPath);
    
    // PHP 8.2+ 可用 ZipArchive::FL_ENC_UTF_8 / FL_ENC_CP437
    // 舊版本需手動轉換:GBK (Windows) / UTF-8 (Linux/Info-ZIP)
    for ($i = 0; $i < $zip->numFiles; $i++) {
        $stat = $zip->statIndex($i);
        $name = $stat['name'];
        
        // 嘗試偵測編碼
        if (!mb_check_encoding($name, 'UTF-8')) {
            $name = iconv('GBK', 'UTF-8', $name); // 常見 Windows 壓縮
        }
        $stat['name'] = $name; // 注意:ZipArchive 無法直接修改名稱,需 extract 後 rename
    }
    
    $zip->extractTo($destDir);
    
    // 二次重命名修正
    foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($destDir)) as $file) {
        $old = $file->getPathname();
        $new = $destDir . DIRECTORY_SEPARATOR . $file->getFilename(); // 簡化,實際需保留目錄結構
        if ($old !== $new) @rename($old, $new);
    }
    $zip->close();
    return true;
}

進階:顯示解壓進度

function unzip_with_progress(string $zipPath, string $destDir, callable $progress): bool {
    $zip = new ZipArchive();
    $zip->open($zipPath);
    $total = $zip->numFiles;
    $count = 0;
    
    for ($i = 0; $i < $total; $i++) {
        $name = $zip->getNameIndex($i);
        $zip->extractTo($destDir, [$name]); // 逐個解壓
        $progress(++$count, $total, $name);
    }
    $zip->close();
    return true;
}

// 使用
unzip_with_progress('big.zip', 'out', function($done, $total, $name) {
    echo "\r[$done/$total] $name";
});

壓縮函數 (對應 66186)

function create_zip(array $files, string $dest, bool $overwrite = false): bool {
    if (file_exists($dest) && !$overwrite) return false;
    
    $zip = new ZipArchive();
    $flags = $overwrite ? ZipArchive::OVERWRITE : ZipArchive::CREATE;
    if ($zip->open($dest, $flags) !== true) return false;
    
    foreach ($files as $file) {
        if (!file_exists($file)) continue;
        $zip->addFile($file, basename($file)); // 僅存檔名,不含路徑
    }
    $zip->close();
    return file_exists($dest);
}

安全檢查清單

  1. 限制上傳 ZIP 大小(upload_max_filesize, post_max_size)。
  2. 解壓前掃描惡意腳本(.php, .phtml, .exe 等)。
  3. 解壓目錄禁止腳本執行權限 (Nginx/Apache 配置)。
  4. 防止 Zip Bomb:限制解壓後總大小、檔案數、壓縮比(>100 警惕)。
  5. 臨時目錄使用隨機名稱,處理完畢立即清理。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved