瀏覽器預設會內嵌顯示 PDF(Content-Type: application/pdf)。要觸發「另存為」下載對話框,需發送:
Content-Type: application/octet-stream 或 application/force-downloadContent-Disposition: attachment; filename="xxx.pdf"<?php
/**
* 強制下載檔案
* @param string $filePath 檔案絕對路徑
* @param string $fileName 下載顯示的檔名(可選,預設取原檔名)
* @return bool 成功輸出 true
*/
function forceDownload($filePath, $fileName = '') {
if (!is_file($filePath) || !is_readable($filePath)) {
header('HTTP/1.1 404 Not Found');
return false;
}
$fileName = $fileName ?: basename($filePath);
$fileSize = filesize($filePath);
// 1. 核心 Header
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . rawurlencode($fileName) . '"');
header('Content-Length: ' . $fileSize);
header('Content-Transfer-Encoding: binary');
header('Accept-Ranges: bytes');
// 2. 快取控制(避免 IE/Edge 緩存導致檔案損壞)
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public'); // HTTP/1.0 相容
header('Expires: 0');
// 3. 輸出檔案內容
// 使用 readfile() 直接輸出,記憶體效率高,支援大檔
readfile($filePath);
return true;
}
// 使用範例
forceDownload('/data/reports/2024_Q1.pdf', '2024年第一季報表.pdf');
?>
| Header | 作用 | 注意事項 |
|---|---|---|
Content-Disposition: attachment | 強制下載而非內嵌 | 檔名需 rawurlencode() 處理中文,雙引號包裹 |
Content-Length | 讓瀏覽器顯示進度條、支援斷點續傳 | 必須精確,壓縮輸出會導致長度不符 |
Cache-Control: must-revalidate | 要求瀏覽器每次重新驗證 | 防止舊版緩存導致下載到過期檔案 |
readfile() | 直接輸出到 PHP 輸出緩衝區 | 不佔用 PHP 記憶體,適合大檔(> 100MB) |
<?php // ← 前面不能有任何字元,含 BOM // 正確:檔案以 <?php 開頭,結尾省略 ?> ?>
// 在 forceDownload() 開頭關閉壓縮
@ini_set('zlib.output_compression', 'Off');
@ini_set('output_buffering', 'Off');
ob_end_clean(); // 清除已有緩衝
# nginx.conf proxy_buffering off; # 關閉代理緩衝,直接串流 proxy_max_temp_file_size 0; # 禁用臨時檔 fastcgi_buffering off; # PHP-FPM 端同步關閉
// 兼容寫法:ASCII fallback + RFC 5987 encoding
$encoded = rawurlencode($fileName);
$ascii = preg_replace('/[^\x20-\x7E]/', '', $fileName) ?: 'download';
header("Content-Disposition: attachment; filename=\"$ascii\"; filename*=UTF-8''$encoded");
大檔(> 500MB)建議實現 Range / 206 Partial Content,或直接用 Nginx ngx_http_sendfile_module / X-Accel-Redirect 讓 Web Server 處理靜態檔傳輸,PHP 僅負責權限驗證。