最直觀的做法是讓 PHP 讀取檔案再輸出:
$file = '/tmp/large.zip';
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.basename($file).'"');
header('Content-Length: '.filesize($file));
readfile($file); // 或 fread 迴圈
缺點:
memory_limit、max_execution_time 限制。$filename = basename($file);
$ua = $_SERVER['HTTP_USER_AGENT'];
$encoded = rawurlencode($filename);
$encoded = str_replace('+', '%20', $encoded); // RFC 5987 空格處理
if (preg_match('/MSIE|Trident|Edge/', $ua)) {
// IE/Edge (舊版)
header('Content-Disposition: attachment; filename="'.$encoded.'"');
} elseif (preg_match('/Firefox/', $ua)) {
// Firefox 支援 RFC 5987
header("Content-Disposition: attachment; filename*=utf-8''$filename");
} else {
// Chrome, Safari, 現代瀏覽器
header('Content-Disposition: attachment; filename="'.$filename.'"');
}
關鍵點:filename* 使用 UTF-8 編碼,避免中文亂碼;舊版 IE 需 urlencode 並將 + 換成 %20。
讓 Web Server(Apache/Nginx/Lighttpd)直接以核心層級 sendfile() 系統呼叫將檔案推送給客戶端,PHP 僅負責權限檢查、Header 產生,完全不讀取檔案內容。
// 安裝:apt-get install libapache2-mod-xsendfile / yum install mod_xsendfile
// 啟用:a2enmod xsendfile
// 配置:XSendFile On / XSendFilePath /tmp/
$file = '/tmp/中文名.tar.gz';
$filename = basename($file);
// ... 中文檔名 Header 處理同上 ...
// 關鍵:只發送 Header,不讀檔
header('X-Sendfile: '.$file);
// 可選:關閉 PHP 輸出緩衝
// header('Content-Length: '.filesize($file)); // 由 Apache 自動補上
// Nginx 配置
location /protected/ {
internal; // 只允許內部重導向
alias /data/files/; // 實際檔案根目錄
}
// PHP
$file = '/data/files/secret.zip';
header('X-Accel-Redirect: /protected/secret.zip');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="secret.zip"');
同理支援 X-Sendfile 或 X-LIGHTTPD-send-file。
| 方案 | PHP 記憶體 | CPU 佔用 | 網路 I/O | 適用場景 |
|---|---|---|---|---|
| readfile() | 高(檔案大小) | 高 | PHP→Client / PHP→Nginx→Client | 小檔、簡單專案 |
| fread 迴圈 + flush | 低(緩衝區大小) | 中 | 同上 | 中型檔、需進度控制 |
| X-Sendfile / X-Accel | 極低(只跑 Header) | 極低 | Kernel→Client (零拷貝) | 大檔、高併發、生產環境 |
$file = realpath($baseDir . '/' . $userInput); if (strpos($file, $baseDir) !== 0) die('非法路徑');internal、Apache XSendFilePath 限制路徑。對於大檔案下載,X-Sendfile (Apache) / X-Accel-Redirect (Nginx) 是標準解法。PHP 僅處理業務邏輯與 Header,將檔案傳輸交給 Web Server 核心層級零拷貝機制,極大降低記憶體、CPU 壓力,並支援斷點續傳(Web Server 原生支援 Range Request)。搭配完善的中文檔名編碼與安全防護,即可構建企業級下載服務。