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

PHP 強制文件下載:任意格式、斷點續傳、大檔案記憶體優化、安全防禦

     編輯:PHP 綜合知識

基礎強制下載

function force_download(string $filePath, string $filename = ''): void {
    if (!is_file($filePath) || !is_readable($filePath)) {
        http_response_code(404);
        exit('File not found');
    }
    
    $filename = $filename ?: basename($filePath);
    $size = filesize($filePath);
    $mime = mime_content_type($filePath) ?: 'application/octet-stream';
    
    header('Content-Type: ' . $mime);
    header('Content-Disposition: attachment; filename="' . rawurlencode($filename) . '"');
    header('Content-Length: ' . $size);
    header('Accept-Ranges: bytes'); // 支援斷點續傳
    header('Cache-Control: private, must-revalidate');
    header('Pragma: private');
    
    // 輸出檔案
    readfile($filePath);
    exit;
}

斷點續傳支援 (HTTP Range)

function force_download_range(string $filePath, string $filename = ''): void {
    if (!is_file($filePath)) { http_response_code(404); exit; }
    
    $size = filesize($filePath);
    $filename = $filename ?: basename($filePath);
    $mime = mime_content_type($filePath) ?: 'application/octet-stream';
    
    $start = 0;
    $end = $size - 1;
    
    if (isset($_SERVER['HTTP_RANGE'])) {
        $range = $_SERVER['HTTP_RANGE'];
        if (preg_match('/bytes=(\d+)-(\d*)/', $range, $m)) {
            $start = (int)$m[1];
            $end = $m[2] ? (int)$m[2] : $size - 1;
            if ($start >= $size || $end >= $size) {
                header('HTTP/1.1 416 Range Not Satisfiable');
                header('Content-Range: bytes */' . $size);
                exit;
            }
            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="' . rawurlencode($filename) . '"');
    header('Content-Length: ' . $length);
    header('Accept-Ranges: bytes');
    header('Cache-Control: private');
    
    $fp = fopen($filePath, 'rb');
    fseek($fp, $start);
    $buffer = 1024 * 8;
    while (!feof($fp) && $length > 0) {
        $read = min($buffer, $length);
        echo fread($fp, $read);
        $length -= $read;
    }
    fclose($fp);
    exit;
}

大檔案記憶體優化:X-Sendfile / X-Accel-Redirect

// Nginx 配置
location /protected/ {
    internal;           # 僅允許內部重定向
    alias /var/www/files/;
}

// PHP
function force_download_xsendfile(string $filePath, string $filename = ''): void {
    $filename = $filename ?: basename($filePath);
    $relPath = '/protected/' . ltrim(str_replace('/var/www/files/', '', $filePath), '/');
    
    header('X-Accel-Redirect: ' . $relPath); // Nginx
    // header('X-Sendfile: ' . $filePath);    // Apache (需 mod_xsendfile)
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . rawurlencode($filename) . '"');
    exit;
}

安全防禦清單

  1. 路徑遍歷:realpath($filePath) 必須在允許目錄內。
  2. 檔案類型驗證:禁止下載 .php, .phtml, .exe 等可執行檔。
  3. 權限控制:結合用戶身份、所有權、下載次數限制。
  4. 防盜鏈:驗證 Referer 或簽名 Token (含過期時間)。
  5. 速率限制:Nginx limit_rate 或 PHP 佇列控制。

Content-Disposition 編碼最佳實踐 (RFC 5987)

header('Content-Disposition: attachment; filename="' . rawurlencode($filename) . '"; filename*=UTF-8\'\'' . rawurlencode($filename));
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved