在 Web 開發中,提供檔案下載功能時,往往需要結合權限判斷,確保只有授權用戶能存取特定檔案。PHP 內建的 readfile() 函數配合適當的 HTTP 標頭,能高效實現這一需求,且不會將檔案完整載入記憶體,適合大檔案傳輸。
<?php
// 取得檔案真實路徑(建議使用本地絕對路徑,避免使用 URL)
$file = get_file_address();
if (file_exists($file)) {
// 設定下載標頭
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
// 關鍵步驟:清空輸出緩衝區,防止檔案開頭出現多餘字元
ob_clean();
flush();
// 輸出檔案內容
readfile($file);
exit;
}
?>
範例中的 get_file_address() 應包含完整的權限檢查:驗證用戶身份、檢查檔案所有權、確認下載授權等。只有通過驗證才返回真實路徑,否則返回 false 或拋出異常。
realpath() 解析路徑,並確認檔案位於允許目錄內。../ 等序列。readfile() 直接輸出到標準輸出,不佔用 PHP 記憶體限制,但需確認 output_buffering 設定不會導致記憶體暴增。若需支援 HTTP Range 請求(續傳、多執行緒下載),需解析 $_SERVER['HTTP_RANGE'],回應 206 Partial Content 狀態碼,並使用 fseek()、fread() 分塊輸出。這屬於進階主題,基本 readfile() 方案已滿足多數場景。
善用 readfile() 搭配完整的標頭控制與權限驗證,即可構建高效、安全的檔案下載功能。記住:永遠不要直接信任用戶輸入的路徑,伺服端必須做嚴格的授權檢查與路徑清理。