IE 瀏覽器對文件下載有獨特行為:中文文件名必須 URL 編碼、下載前必須清空緩存 Header、否則提示「目標主機無法訪問」。本文提供兩套經過實戰驗證的完整下載代碼。
| 問題 | IE 表現 | 根因 |
|---|---|---|
| 中文文件名亂碼 | 下載文件名顯示為亂碼或截斷 | IE 要求 Content-Disposition 中文件名必須 URL 編碼 |
| 提示「無法訪問」 | 彈窗錯誤,無法下載 | IE 對 Cache-Control/Pragma 嚴格,需顯式設置允許緩存 |
| SSL 下載失敗 | HTTPS 下無法下載 | IE 禁止 HTTPS 下的 no-cache 下載,需 Pragma: public |
<?php
$path_c = '/data/export/报表_2024.csv';
$filename = '报表_2024.csv';
$type = 'text/csv';
// 關鍵:清空 IE 問題 Header
header("Pragma: "); // 必須為空,否則 IE 報錯
header("Cache-Control: "); // 必須為空
header("Content-type: $type");
header("Accept-Ranges: bytes");
header("Content-Transfer-Encoding: base64");
header("Accept-Length: " . filesize($path_c));
// 關鍵:中文文件名 urlencode
header("Content-Disposition: attachment; filename=" . urlencode($filename));
readfile($path_c);
exit;
?>
<?php
$file = '/data/export/报表_2024.csv';
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
// 關鍵:中文文件名 urlencode + basename 防路徑遍歷
header('Content-Disposition: attachment; filename=' . urlencode(basename($file)));
header('Content-Transfer-Encoding: binary'); // 二進制傳輸
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); // IE 必須
header('Pragma: public'); // IE HTTPS 必須
header('Content-Length: ' . filesize($file));
// 清空輸出緩衝區,防止前置輸出污染文件
ob_clean();
flush();
readfile($file);
exit;
?>
// 緩存控制組合拳(IE 專用)
header('Expires: 0'); // 立即過期
header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); // 強制重新驗證
header('Pragma: public'); // HTTP/1.0 兼容,HTTPS 下允許下載
// 文件傳輸聲明
header('Content-Description: File Transfer'); // 語義化
header('Content-Type: application/octet-stream'); // 強制下載而非預覽
header('Content-Disposition: attachment; filename=...'); // 觸發「另存為」對話框
header('Content-Transfer-Encoding: binary'); // 二進制流,不做編碼轉換
header('Content-Length: ' . filesize($file)); // 精確文件大小,支援斷點續傳
<?php
/**
* 通用文件下載函數(支援中文文件名、大文件、斷點續傳、IE/Edge/現代瀏覽器)
*/
function download_file($file_path, $download_name = null, $mime_type = 'application/octet-stream') {
if (!is_file($file_path) || !is_readable($file_path)) {
http_response_code(404);
exit('File not found');
}
$download_name = $download_name ?: basename($file_path);
$file_size = filesize($file_path);
// 防止腳本超時
set_time_limit(0);
// 緩存控制(兼容所有瀏覽器)
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
// 文件信息
header('Content-Description: File Transfer');
header('Content-Type: ' . $mime_type);
header('Content-Disposition: attachment; filename="' . rawurlencode($download_name) . '"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . $file_size);
// 支援斷點續傳 (HTTP Range)
if (isset($_SERVER['HTTP_RANGE'])) {
$range = $_SERVER['HTTP_RANGE'];
list($unit, $range) = explode('=', $range);
if ($unit === 'bytes') {
list($start, $end) = explode('-', $range);
$start = intval($start);
$end = $end ? intval($end) : $file_size - 1;
$end = min($end, $file_size - 1);
$length = $end - $start + 1;
header('HTTP/1.1 206 Partial Content');
header('Content-Range: bytes ' . $start . '-' . $end . '/' . $file_size);
header('Content-Length: ' . $length);
$fp = fopen($file_path, 'rb');
fseek($fp, $start);
while (!feof($fp) && $length > 0) {
$buffer = fread($fp, min(8192, $length));
echo $buffer;
$length -= strlen($buffer);
}
fclose($fp);
exit;
}
}
// 普通全量下載
ob_clean();
flush();
readfile($file_path);
exit;
}
// 使用示例
download_file('/data/exports/用戶報表_2024.xlsx', '用戶報表_2024.xlsx', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
?>
file_get_contents() 讀入內存,必須用 readfile() 或流式輸出proxy_buffering off; 否則大文件會被緩存到磁盤再傳輸session_write_close() 釋放鎖,否則同用戶並發請求卡死urlencode() 還是 rawurlencode()?現代瀏覽器支援 RFC 5987 格式:filename*=UTF-8''encoded_name,建議雙重兼容掌握這套方案,能覆蓋 99% 的 PHP 文件下載場景,徹底告別 IE 下載疑難雜症。