首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 文件下載在 IE 中亂碼與緩存問題完美解決方案

     編輯:PHP 相關知識

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;
?>

Header 參數詳細解釋

// 緩存控制組合拳(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');
?>

常見坑點提醒

  1. 輸出前無任何輸出:包括 BOM、空白行、echo、var_dump,否則文件損壞
  2. 大文件內存溢出:避免 file_get_contents() 讀入內存,必須用 readfile() 或流式輸出
  3. Nginx 代理干擾:需設置 proxy_buffering off; 否則大文件會被緩存到磁盤再傳輸
  4. Session 阻塞:下載前調用 session_write_close() 釋放鎖,否則同用戶並發請求卡死
  5. 文件名編碼:urlencode() 還是 rawurlencode()?現代瀏覽器支援 RFC 5987 格式:filename*=UTF-8''encoded_name,建議雙重兼容

掌握這套方案,能覆蓋 99% 的 PHP 文件下載場景,徹底告別 IE 下載疑難雜症。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved