核心標頭組合
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . basename($file) . '"');
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
分塊輸出大檔案(避免記憶體耗盡)
function downloadFile($filePath) {
if (!is_file($filePath)) { http_response_code(404); return; }
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $filePath);
finfo_close($finfo);
header('Content-Type: ' . $mime);
header('Content-Disposition: attachment; filename="' . basename($filePath) . '"');
header('Content-Length: ' . filesize($filePath));
header('Accept-Ranges: bytes'); // 支援斷點續傳
$handle = fopen($filePath, 'rb');
while (!feof($handle)) {
echo fread($handle, 8192);
ob_flush();
flush();
}
fclose($handle);
}
MIME 類型映射表(備選方案)
$mimeTypes = [
'pdf' => 'application/pdf',
'zip' => 'application/zip',
'doc' => 'application/msword',
'xls' => 'application/vnd.ms-excel',
'png' => 'image/png',
'jpg' => 'image/jpeg',
// ...更多
];
$ext = strtolower(pathinfo($filePath, PATHINFO_EXTENSION));
$mime = $mimeTypes[$ext] ?? 'application/octet-stream';
安全檢查清單
- 路徑遍歷防護:
realpath() 鎖定在允許目錄內
- 權限驗證:下載前檢查用戶授權
- 速率限制:防止頻寬濫用
- 日誌記錄:審計下載行為