文件下載功能看似簡單,但響應頭設置不當會導致文件名亂碼、瀏覽器直接預覽而非下載、大文件傳輸中斷等問題。本文給出生產級的標準頭部配置,並解析每個頭部的作用與避坑點。
// $file 為服務器絕對路徑
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header('Content-Length: ' . filesize($file));
readfile($file); // 直接輸出文件流,內存友好
| 頭部 | 作用 | 關鍵點 |
|---|---|---|
Content-Type: application/octet-stream |
聲明二進制流,強制瀏覽器「另存為」而非嘗試預覽 | 通用 MIME,適用於所有文件類型;若需針對性預覽可改為具體 MIME |
Content-Disposition: attachment; filename="..." |
指定下載模式與默認文件名 | 雙引號包裹文件名防空格截斷;中文名需 urlencode() 或 RFC5987 格式 |
Content-Length: 文件字節數 |
告知瀏覽器文件大小,支援進度條、斷點續傳 | 必須精確,否則下載進度異常或文件損壞 |
readfile() |
直接將文件流輸出到輸出緩衝區 | 不佔用 PHP 內存讀取整個文件,適合大文件;自動處理緩衝刷新 |
$filename = basename($file);
$encoded = urlencode($filename);
$encoded = str_replace('+', '%20', $encoded); // 修正空格編碼
// 現代瀏覽器支援 RFC 5987
header("Content-Disposition: attachment; filename*=UTF-8''{$encoded}; filename=\"{$filename}\"");
// 兼容舊版 IE(<9)可加判斷 User-Agent 降級
// Nginx 配置需 location /protected/ { internal; alias /data/files/; }
header('X-Accel-Redirect: /protected/' . basename($file));
header('Content-Disposition: attachment; filename="..."');
exit;
$fp = fopen($file, 'rb');
while (!feof($fp)) {
echo fread($fp, 8192);
ob_flush(); flush();
}
fclose($fp);
set_time_limit(0);
ini_set('memory_limit', '64M');
header('Content-Type: application/pdf') 導致瀏覽器直接打開而非下載Content-Length,下載進度條卡在 0% 或無法斷點續傳file_get_contents 讀取大文件導致內存溢出掌握這套標準頭部組合與優化手段,即可應對從 KB 到 GB 級別的各類文件下載場景,兼容性與性能雙保障。