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

PHP 強制瀏覽器下載圖片而非預覽:Content-Disposition 標頭正確設定

     編輯:PHP 相關知識

PHP 觸發檔案下載:Content-Disposition: attachment、MIME 類型、大檔案支援與安全檢查

預設瀏覽器會直接顯示圖片、PDF。若需強制下載(如相簿打包、報表匯出),必須送出正確的 HTTP 標頭。本文提供完整、安全的下載實作。

基本下載腳本

<?php
// download.php
$file = __DIR__ . '/files/' . basename($_GET['file'] ?? ''); // 僅允許檔名,防目錄遍歷
if (!is_file($file)) { http_response_code(404); exit('File not found'); }

$filename = basename($file);
$size = filesize($file);
$mime = mime_content_type($file) ?: 'application/octet-stream';

header('Content-Description: File Transfer');
header('Content-Type: ' . $mime);
header('Content-Disposition: attachment; filename="' . rawurlencode($filename) . '"; filename*=UTF-8\'\'' . rawurlencode($filename));
header('Content-Length: ' . $size);
header('Content-Transfer-Encoding: binary');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Expires: 0');

readfile($file); // PHP 內建分塊輸出,不佔記憶體
exit;
?>

HTML 連結

<a href="download.php?file=photo.jpg">下載照片</a>
<a href="download.php?file=report.pdf">下載報表</a>

關鍵標頭說明

標頭用途關鍵值
Content-Disposition指示瀏覽器處理方式attachment; filename="..."
Content-TypeMIME 類型mime_content_type() 自動偵測
Content-Length檔案大小,支援進度條filesize()
filename*RFC 5987 編碼,支援中文檔名UTF-8''encoded_name

大檔案與記憶體優化

  • readfile() 內建 8KB 緩衝區串流輸出,**不會**將整個檔案載入記憶體。
  • 若需更精確控制,可用 fopen() + fread() 迴圈 + ob_flush()/flush()。
  • 關閉輸出緩衝:@ini_set('output_buffering', 'Off'); @ini_set('zlib.output_compression', 'Off');。

安全性檢查清單

  1. 路徑遍歷防護:basename()、realpath() 確認在允許目錄下。
  2. 權限控制:驗證使用者是否有權下載(登入、擁有者、權限表)。
  3. 檔案類型白名單:僅允許 jpg,png,pdf,zip 等,拒絕 .php,.sh,.exe。
  4. 防止熱連結:檢查 Referer 或簽名 URL (簽名含過期時間)。
  5. 速率限制:同一 IP 每分鐘下載次數上限。

進階:X-Sendfile / X-Accel-Redirect (Nginx/Apache)

極大檔案 (GB 級) 由 Web Server 直接傳送,PHP 僅驗證權限:

// PHP
header('X-Accel-Redirect: /protected/files/' . $filename); // Nginx
header('X-Sendfile: /var/www/protected/files/' . $filename); // Apache (mod_xsendfile)
header('Content-Disposition: attachment; filename="' . rawurlencode($filename) . '"');
exit;

// Nginx 設定
location /protected/ {
    internal;
    alias /var/www/protected/;
}

優點:零 PHP 記憶體/CPU 佔用、支援斷點續傳、高併發。

常見問題

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
現象原因解決
瀏覽器仍預覽Content-Type 為 image/*, 且無 attachment確認送出 Content-Disposition: attachment
中文檔名亂碼未用 RFC 5987 filename*雙重提供 filename 與 filename*
下載檔案 0 bytes路徑錯誤、權限不足、readfile 失敗檢查 is_file()、is_readable()
記憶體耗盡用 file_get_contents() 載入大檔改用 readfile() 或 X-Sendfile