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

PHP 檔案下載兩大實作方式與代碼範例

     編輯:PHP 相關知識

前言

在 Web 開發中,提供檔案下載是常見需求。PHP 透過 `header()` 函式控制 HTTP 回應標頭,即可實現瀏覽器「另存新檔」提示。本文介紹兩種主流實作方式,並說明關鍵標頭用途。

方式一:基礎 `header()` 直接輸出

適用於小型檔案、不需斷點續傳的簡單場景。

<?php
$file = '/path/to/your/file.pdf'; // 檔案絕對路徑
$filename = 'download.pdf';       // 瀏覽器顯示的檔名

if (!file_exists($file)) {
    header('HTTP/1.1 404 Not Found');
    exit('檔案不存在');
}

// 關鍵標頭設定
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($file));
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Expires: 0');

// 輸出檔案內容
readfile($file);
exit;
?>

關鍵標頭解析

  • Content-Type: application/octet-stream:通用二進位流,強制瀏覽器下載而非嘗試開啟。
  • Content-Disposition: attachment; filename="...":指定下載檔名,`attachment` 表示作為附件處理。
  • Content-Length:告知瀏覽器檔案大小,支援進度條顯示。
  • Cache-Control / Pragma:控制快取行為,避免代理伺服器干擾。

方式二:支援斷點續傳(Range Requests)

大型檔案下載建議支援 HTTP Range 標頭,允許使用者暫停/繼續下載。

<?php
$file = '/path/to/large/video.mp4';
$filename = 'video.mp4';

if (!file_exists($file)) {
    header('HTTP/1.1 404 Not Found');
    exit;
}

$size   = filesize($file);
$ext    = pathinfo($file, PATHINFO_EXTENSION);
$mime   = 'application/octet-stream'; // 或使用 mime_content_type($file)

// 處理 Range 標頭
$start = 0;
$end   = $size - 1;
header('Accept-Ranges: bytes');
if (isset($_SERVER['HTTP_RANGE'])) {
    $range = $_SERVER['HTTP_RANGE'];
    if (preg_match('/bytes=(\d+)-(\d*)/', $range, $matches)) {
        $start = intval($matches[1]);
        $end   = isset($matches[2]) && $matches[2] ? intval($matches[2]) : $size - 1;
        header('HTTP/1.1 206 Partial Content');
        header('Content-Range: bytes ' . $start . '-' . $end . '/' . $size);
    }
}

$length = $end - $start + 1;

header('Content-Type: ' . $mime);
header('Content-Disposition: attachment; filename="' . $filename . '"');
header('Content-Length: ' . $length);
header('Content-Transfer-Encoding: binary');

// 以區塊讀取輸出,避免記憶體耗盡
$fp = fopen($file, 'rb');
fseek($fp, $start);
while (!feof($fp) && $length > 0) {
    $chunk = min(8192, $length);
    echo fread($fp, $chunk);
    $length -= $chunk;
    flush();
}
fclose($fp);
exit;
?>

常見陷阱與最佳實踐

  • 輸出緩衝區干擾:在 `header()` 前確保無任何輸出(含空白、BOM),或使用 `ob_clean()` 清空緩衝。
  • 中文檔名編碼:`filename` 建議使用 RFC 5987 格式:`filename*=UTF-8''` . rawurlencode($filename) ,兼容舊瀏覽器可同時提供 `filename` 參數。
  • 大檔案記憶體:切勿使用 `file_get_contents()` 一次讀入,必須採用 `readfile()` 或分塊 `fread()` 輸出。
  • 權限控管:實際專案中應在輸出前驗證使用者權限、記錄下載日誌。
  • Nginx X-Accel-Redirect / Apache X-Sendfile:高流量生產環境建議交由 Web Server 直接傳送檔案,PHP 僅負責驗權並回傳 `X-Accel-Redirect` 標頭,大幅降低 PHP 進程佔用。

延伸閱讀

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