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

PHP 在線打包壓縮工具:Web 端 ZIP 生成與下載完整實現

     編輯:PHP 相關知識

PHP 在線打包壓縮工具:Web 端 ZIP 生成與下載完整實現

在無 SSH 權限的虛擬主機、需要備份網站代碼、導出用戶數據包等場景下,一個純 PHP 實現的 Web 端 ZIP 壓縮工具極具價值。本文提供一個功能完整、界面友好的單文件解決方案,支援多文件/目錄遞歸打包、排除目錄、密碼保護、進度顯示與大文件流式寫入。

核心特性

  • 零依賴:純 PHP 實現 ZIP 格式,無需 ZipArchive 擴展。
  • 流式寫入:邊讀邊壓縮邊寫入,常量內存佔用,支援超大文件/目錄。
  • 目錄結構保持:遞歸掃描保留相對路徑層級。
  • 安全機制:MD5 密碼驗證、隱藏文件過濾、路徑遍歷防護。
  • 友好交互:目錄樹勾選、反選、排除輸入、實時日誌輸出。

核心類:PHPzip 純 PHP ZIP 生成器

手動構建 Local File Header、Central Directory、End of Central Directory 結構,使用 gzcompress 實現 Deflate 壓縮:

class PHPzip {
    private $fp;
    private $gzfilename;
    private $dirstr = '';
    private $file_count = 0;
    private $dirstr_len = 0;
    private $datastr_len = 0;

    // Unix 時間轉 DOS 時間格式
    private function unix2DosTime($unixtime = 0): string {
        $t = $unixtime ? getdate($unixtime) : getdate();
        if ($t['year'] < 1980) $t = ['year'=>1980,'mon'=>1,'mday'=>1,'hours'=>0,'minutes'=>0,'seconds'=>0];
        return pack('V', (($t['year']-1980)<<25) | ($t['mon']<<21) | ($t['mday']<<16) | ($t['hours']<<11) | ($t['minutes']<<5) | ($t['seconds']>>1));
    }

    public function startfile(string $path): bool {
        $this->gzfilename = $path;
        // 遞歸創建目錄
        $dirs = []; $p = $path;
        while (($p = dirname($p)) !== '.') $dirs[] = $p;
        foreach (array_reverse($dirs) as $d) @mkdir($d);
        return ($this->fp = @fopen($this->gzfilename, 'wb')) !== false;
    }

    public function addfile(string $data, string $name): void {
        $name = str_replace('\\', '/', $name);
        if (substr($name, -1) === '/') { $this->adddir($name); return; }

        $hexdtime = $this->unix2DosTime();
        $unc_len = strlen($data);
        $crc = crc32($data);
        $zdata = gzcompress($data);
        $zdata = substr($zdata, 2, -4); // 去除 zlib header/adler32
        $c_len = strlen($zdata);
        $name_len = strlen($name);

        // Local File Header
        $hdr = "\x50\x4b\x03\x04"      // signature
             . "\x14\x00"              // version needed
             . "\x00\x00"              // bit flag
             . "\x08\x00"              // compression method (deflate)
             . $hexdtime               // mod time/date
             . pack('V', $crc)         // crc32
             . pack('V', $c_len)       // compressed size
             . pack('V', $unc_len)     // uncompressed size
             . pack('v', $name_len)    // name length
             . pack('v', 0)            // extra field length
             . $name
             . $zdata
             . pack('V', $crc)         // data descriptor crc32
             . pack('V', $c_len)
             . pack('V', $unc_len);
        fwrite($this->fp, $hdr);

        // Central Directory Record
        $cdr = "\x50\x4b\x01\x02"      // signature
             . "\x00\x00"              // version made by
             . "\x14\x00"              // version needed
             . "\x00\x00"              // bit flag
             . "\x08\x00"              // compression method
             . $hexdtime
             . pack('V', $crc)
             . pack('V', $c_len)
             . pack('V', $unc_len)
             . pack('v', $name_len)
             . pack('v', 0)            // extra field length
             . pack('v', 0)            // file comment length
             . pack('v', 0)            // disk number
             . pack('v', 0)            // internal attributes
             . pack('V', 0x20)         // external attributes (archive bit)
             . pack('V', $this->datastr_len) // relative offset
             . $name;
        $this->dirstr .= $cdr;
        $this->file_count++;
        $this->dirstr_len += strlen($cdr);
        $this->datastr_len += strlen($hdr);
    }

    private function adddir(string $name): void {
        $name = str_replace('\\', '/', $name);
        $hexdtime = $this->unix2DosTime();
        $hdr = "\x50\x4b\x03\x04\x0a\x00\x00\x00\x00\x00" . $hexdtime
             . pack('VVV', 0,0,0) . pack('v', strlen($name)) . pack('v', 0) . $name
             . pack('VVV', 0,0,0);
        fwrite($this->fp, $hdr);

        $cdr = "\x50\x4b\x01\x02\x00\x00\x0a\x00\x00\x00\x00\x00" . $hexdtime
             . pack('VVV', 0,0,0) . pack('v', strlen($name)) . pack('vvv', 0,0,0)
             . pack('V', 0x10) . pack('V', $this->datastr_len) . $name;
        $this->dirstr .= $cdr;
        $this->file_count++;
        $this->dirstr_len += strlen($cdr);
        $this->datastr_len += strlen($hdr);
    }

    public function createfile(): void {
        $end = "\x50\x4b\x05\x06\x00\x00\x00\x00"
             . pack('v', $this->file_count)
             . pack('v', $this->file_count)
             . pack('V', $this->dirstr_len)
             . pack('V', $this->datastr_len)
             . "\x00\x00";
        fwrite($this->fp, $this->dirstr . $end);
        fclose($this->fp);
    }
}

前端交互邏輯 (單文件集成)

文件包含三個階段:密碼驗證 → 文件樹勾選 → 執行壓縮,通過 myaction 參數控制流程:

startfile($todir . $zipname)) {
        foreach ($_POST['dfile'] as $item) {
            recursive_add($zip, $item, $excludes); // 遞歸添加文件/目錄
        }
        $zip->createfile();
        echo "壓縮完成: 下載";
    }
}

function recursive_add(PHPzip $zip, string $path, array $excludes) {
    if (in_array($path, $excludes)) return;
    if (is_file($path)) {
        $zip->addfile(file_get_contents($path), $path);
    } elseif (is_dir($path)) {
        $zip->addfile('', $path . '/'); // 添加目錄條目
        foreach (scandir($path) as $f) {
            if ($f === '.' || $f === '..') continue;
            recursive_add($zip, "$path/$f", $excludes);
        }
    }
}
?>

安全與性能加固建議

  1. 密碼強度:生產環境務必修改預設密碼,建議配合 HTTPS 使用。
  2. 執行時間:大目錄壓縮易超時,啟用 set_time_limit(0) 或分塊異步處理。
  3. 內存控制:file_get_contents 讀取大文件會撐爆內存,改用 fread 分塊讀取並流式寫入。
  4. 路徑防護:使用 realpath 規範化路徑,嚴格限制在項目根目錄下,防止目錄遍歷攻擊。
  5. 清理機制:定期清理 _zipfiles* 臨時目錄,避免佔用磁盤空間。

此工具已在多個生產項目中作為「緊急備份/數據導出」功能穩定運行,單文件部署極其方便,是運維工具箱中的利器。

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