在 PHP 開發中,實現檔案下載功能是常見需求。本文詳細解析兩種主流實現方式,並說明關鍵 Header 設定與注意事項,幫助開發者選擇適合專案的方案。
此方法透過設定完整的 HTTP Header,瀏覽器會彈出「另存新檔」對話框,適合任意類型檔案,且支援斷點續傳與大檔案傳輸。
<?php
$filepath = '/path/to/your/file.zip'; // 檔案實際路徑
$filename = basename($filepath); // 取得檔案名稱
// 1. 宣告輸出為二進位串流
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
// 2. 指定下載檔名,防止中文檔名亂碼建議使用 urlencode
header('Content-Disposition: attachment; filename="' . urlencode($filename) . '"');
header('Content-Transfer-Encoding: binary');
// 3. 快取控制:強制瀏覽器不快取,確保每次都是最新內容
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
// 4. 告知瀏覽器檔案大小,支援進度條顯示
header('Content-Length: ' . filesize($filepath));
// 5. 直接輸出檔案內容到輸出緩衝區
readfile($filepath);
exit(); // 確保腳本結束,避免輸出多餘內容
?>
此方法根據副檔名動態設定 MIME 類型,程式碼較精簡,適合已知檔案類型的情境。
<?php
$filename = '/path/to/document.pdf';
$fileinfo = pathinfo($filename);
// 根據副檔名設定 MIME 類型(實際專案建議使用 mime_content_type() 或 finfo)
$mimeTypes = [
'pdf' => 'application/pdf',
'jpg' => 'image/jpeg',
'png' => 'image/png',
'zip' => 'application/zip',
// ... 可自行擴充
];
$ext = strtolower($fileinfo['extension']);
$mime = $mimeTypes[$ext] ?? 'application/octet-stream';
header('Content-type: ' . $mime);
header('Content-Disposition: attachment; filename="' . urlencode($fileinfo['basename']) . '"');
header('Content-Length: ' . filesize($filename));
readfile($filename);
exit();
?>
| 特性 | 方法一 | 方法二 |
|---|---|---|
| 通用性 | 極高,不依賴副檔名 | 需維護 MIME 對照表 |
| 中文檔名支援 | 需自行 urlencode | 同上 |
| 大檔案效能 | 優(readfile 串流) | 優(同用 readfile) |
| 程式碼複雜度 | 稍多 | 精簡 |
realpath() 驗證並在允許目錄內,防止目錄遍歷攻擊。urlencode() 編碼檔名,並在前端以 decodeURIComponent() 解碼,相容性最佳。memory_limit 設定合理,readfile() 不會將檔案載入記憶體,但輸出緩衝區仍需注意。$_SERVER['HTTP_RANGE'] 並發送 206 Partial Content,可參考 RFC 7233。綜上所述,方法一 因其通用性與標準合規性,建議作為專案預設下載實作;方法二 適合快速原型或檔案類型固定的內部系統。實際部署時,請務必加入權限檢查與路徑清理,確保資料安全。