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

PHP 實現文件下載限速功能:控制輸出頻寬完整代碼

     編輯:PHP 相關知識

核心實現原理

通過分塊讀取文件、控制每秒輸出位元組數、配合 sleep() 暫停,實現應用層下載限速。適用於共享頻寬環境防止單用戶佔滿帶寬。

完整限速下載代碼

<?php
/**
 * 文件限速下載
 * 
 * @param string $local_file  服務器本地文件路徑
 * @param string $download_file 用戶下載時的文件名
 * @param float  $download_rate_kbps 限速 KB/s (如 20.5 = 20.5 KB/s)
 */
function downloadWithThrottle($local_file, $download_file, $download_rate_kbps = 20.5) {
    // 1. 文件存在性檢查
    if (!file_exists($local_file) || !is_file($local_file)) {
        header('HTTP/1.1 404 Not Found');
        die('Error: File not found!');
    }
    
    $file_size = filesize($local_file);
    
    // 2. 發送 HTTP 頭
    header('Cache-Control: private, must-revalidate');
    header('Content-Type: application/octet-stream');
    header('Content-Length: ' . $file_size);
    header('Content-Disposition: attachment; filename="' . $download_file . '"');
    header('Accept-Ranges: bytes'); // 支援斷點續傳(需額外實現 Range 處理)
    
    // 3. 刷新輸出緩衝
    flush();
    
    // 4. 打開文件流
    $handle = fopen($local_file, 'rb');
    if (!$handle) {
        die('Error: Cannot open file!');
    }
    
    // 5. 計算每次讀取塊大小(位元組)
    $chunk_size = (int)round($download_rate_kbps * 1024);
    if ($chunk_size < 1024) $chunk_size = 1024; // 最小 1KB
    
    // 6. 分塊輸出循環
    while (!feof($handle)) {
        // 讀取塊
        $buffer = fread($handle, $chunk_size);
        if ($buffer === false) break;
        
        // 輸出到瀏覽器
        echo $buffer;
        
        // 強制刷新輸出緩衝
        flush();
        
        // 限速:暫停 1 秒(根據實際需求可調整為 usleep 微秒級控制)
        sleep(1);
        
        // 檢查客戶端是否斷開連接
        if (connection_aborted()) {
            break;
        }
    }
    
    // 7. 關閉文件句柄
    fclose($handle);
}

// 使用範例
$local_file = '/data/downloads/large-file.zip';
$download_file = 'user-download-name.zip';
$download_rate = 50; // 50 KB/s

downloadWithThrottle($local_file, $download_file, $download_rate);

進階版:支援斷點續傳

 0) {
        $read_size = min($chunk_size, $remaining);
        $buffer = fread($handle, $read_size);
        if ($buffer === false) break;
        
        echo $buffer;
        flush();
        
        $remaining -= strlen($buffer);
        
        if ($remaining > 0) {
            sleep(1); // 或 usleep 精確控制
        }
        
        if (connection_aborted()) break;
    }
    
    fclose($handle);
}

關鍵參數調優建議

參數建議值說明
chunk_sizerate_kbps × 1024每秒讀取位元組數,太小增加系統調用開銷
sleep(1)1 秒粒度較粗,精確控制可用 usleep(1000000 * chunk / rate)
flush()每次循環強制輸出緩衝區數據到客戶端
connection_aborted()每次循環檢查客戶端取消下載時及時釋放資源

生產環境注意事項

⚠ 重要限制:

  • PHP 層面限速 佔用 PHP-FPM/Apache 進程,高併發下會耗盡工作進程
  • 大文件長時間下載易觸發 max_execution_time,需 set_time_limit(0)
  • 輸出緩衝區(output_buffering、nginx proxy_buffering)可能抵消 flush() 效果

🏭 企業級方案:

  • Nginx limit_rate / limit_rate_after 模組(內核級,高效)
  • CDN 邊緣節點限速
  • 對象存儲(OSS/S3)預簽名 URL + 帶寬限制
  • 專用下載服務(Go/Node.js 異步 I/O)

微秒級精確限速實現

 0) {
        usleep((int)$sleep_time);
    }
}

// 在循環中使用
$start_time = microtime(true);
$total_sent = 0;
$rate_bps = 50 * 1024; // 50 KB/s

while (!feof($handle)) {
    $buffer = fread($handle, 8192);
    echo $buffer;
    flush();
    $total_sent += strlen($buffer);
    preciseThrottle($total_sent, $rate_bps, $start_time);
    
    if (connection_aborted()) break;
}
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved