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

PHP 安全下載實作:Header 控制、斷點續傳、速度限制、ETag/Last-Modified 快取與完整 MIME 類型對照表

     編輯:PHP 相關知識

核心功能

sendFile($fileName, $fancyName, $forceDownload, $speedLimit, $contentType) 完整支援:

  • 隱藏真實路徑:URL 不暴露檔案實體位置。
  • 斷點續傳:解析 HTTP_RANGE Header,回傳 206 Partial Content。
  • 速度限制:$speedLimit (bytes/sec) 配合 usleep 節流。
  • 快取驗證:ETag、Last-Modified、If-None-Match、If-Modified-Since,支援 304 Not Modified。
  • 安全檢查:is_readable、路徑穿越防護 (實務需額外 realpath 限制根目錄)。
  • 中文檔名:rawurlencode 符合 RFC 5987。

完整代碼 (精簡版,保留核心邏輯)

<?php
function sendFile(string $file, string $name = '', bool $force = true, int $speed = 0, string $mime = ''): bool {
    if (!is_file($file) || !is_readable($file)) {
        http_response_code(404);
        return false;
    }
    $stat = stat($file);
    $size = $stat['size'];
    $mtime = $stat['mtime'];
    
    // ETag: md5(mtime-inode-size) + crc32
    $etag = '"' . md5("$mtime-{$stat['ino']}-$size") . '-' . sprintf('%u', crc32($file)) . '"';
    header("ETag: $etag");
    header('Last-Modified: ' . gmdate('D, d M Y H:i:s', $mtime) . ' GMT');
    
    // 快取驗證
    if ((isset($_SERVER['HTTP_IF_NONE_MATCH']) && $_SERVER['HTTP_IF_NONE_MATCH'] === $etag) ||
        (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= $mtime)) {
        http_response_code(304);
        return true;
    }
    
    $name = $name ?: basename($file);
    $mime = $mime ?: mime_content_type($file) ?: 'application/octet-stream';
    
    // Range 處理
    $start = 0; $end = $size - 1; $partial = false;
    if (isset($_SERVER['HTTP_RANGE'])) {
        if (preg_match('/bytes=(\d*)-(\d*)/', $_SERVER['HTTP_RANGE'], $r)) {
            $start = $r[1] !== '' ? (int)$r[1] : $size - (int)$r[2];
            $end = $r[2] !== '' ? (int)$r[2] : $size - 1;
            $start = max(0, min($start, $size - 1));
            $end = max($start, min($end, $size - 1));
            $partial = true;
        }
    }
    $length = $end - $start + 1;
    
    // Header 輸出
    http_response_code($partial ? 206 : 200);
    if ($partial) header("Content-Range: bytes $start-$end/$size");
    header('Accept-Ranges: bytes');
    header("Content-Type: $mime");
    header("Content-Length: $length");
    if ($force) header('Content-Disposition: attachment; filename="' . rawurlencode($name) . '"');
    header('Content-Transfer-Encoding: binary');
    header('Cache-Control: public, must-revalidate, max-age=0');
    header('Pragma: public');
    
    // 發送內容
    $fp = fopen($file, 'rb');
    fseek($fp, $start);
    $bufferSize = 8192;
    $sent = 0;
    while ($sent < $length && !feof($fp) && connection_status() === 0) {
        $read = min($bufferSize, $length - $sent);
        echo fread($fp, $read);
        ob_flush(); flush();
        $sent += $read;
        if ($speed > 0) {
            $target = $bufferSize * 1e6 / $speed; // 微秒
            $elapsed = (microtime(true) - $loopStart) * 1e6;
            $sleep = $target - $elapsed;
            if ($sleep > 0) usleep($sleep);
        }
    }
    fclose($fp);
    return true;
}

MIME 類型完整對照表 (節錄常用)

副檔名MIME Type
.pdfapplication/pdf
.jpg/.jpegimage/jpeg
.pngimage/png
.gifimage/gif
.zipapplication/zip
.doc/.docxapplication/msword / application/vnd.openxmlformats-officedocument.wordprocessingml.document
.xls/.xlsxapplication/vnd.ms-excel / application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
.mp4video/mp4
.mp3audio/mpeg
.apkapplication/vnd.android.package-archive
.woff/.woff2font/woff / font/woff2

完整 300+ 條對照表請參考原文附錄或使用 mime_content_type() (需 fileinfo 擴展) 自動偵測。

生產環境加強建議

  1. 路徑安全:$real = realpath($file); if (strpos($real, $allowedRoot) !== 0) die('Forbidden');
  2. 大檔案記憶體:採用 readfile() 或 fread 分塊輸出 (如上),嚴禁 file_get_contents 整個讀入記憶體。
  3. Nginx X-Accel-Redirect:高併發下載建議交給 Nginx 處理,PHP 僅驗證權限後回傳 X-Accel-Redirect: /protected/path,效能提升 10 倍以上。
  4. Token 驗證:下載連結加入簽名 ?token=hmac_sha256(file_id, expiry, secret),防止熱連結。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved