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

PHP 斷點續傳下載類 FileDownload:支援 HTTP Range 標頭完整實作

     編輯:PHP 綜合知識

HTTP 斷點續傳原理

  • Range (請求頭):Range: bytes=200-300 指定位元組區間。
  • Content-Range (回應頭):Content-Range: bytes 200-300/801 表明回應區間與總大小。
  • 狀態碼:206 Partial Content 表示部分內容;200 OK 表示完整檔案。
  • Accept-Ranges:Accept-Ranges: bytes 告知客戶端支援斷點續傳。

完整類別代碼 (FileDownload.class.php)

<?php
class FileDownload {
    private $_speed = 512; // KB/s

    public function download($file, $name = '', $reload = false) {
        if (!file_exists($file)) return;

        $name = $name ?: basename($file);
        $fp = fopen($file, 'rb');
        $file_size = filesize($file);
        $ranges = $this->getRange($file_size);

        header('Cache-Control: public');
        header('Content-Type: application/octet-stream');
        header('Content-Disposition: attachment; filename="' . $name . '"');

        if ($reload && $ranges !== null) {
            // 斷點續傳回應
            header('HTTP/1.1 206 Partial Content');
            header('Accept-Ranges: bytes');
            header(sprintf('Content-Length: %u', $ranges['end'] - $ranges['start']));
            header(sprintf('Content-Range: bytes %s-%s/%s', $ranges['start'], $ranges['end'], $file_size));
            fseek($fp, $ranges['start']);
        } else {
            header('HTTP/1.1 200 OK');
            header('Content-Length: ' . $file_size);
        }

        while (!feof($fp)) {
            echo fread($fp, round($this->_speed * 1024));
            ob_flush();
            // sleep(1); // 測試用:模擬限速
        }
        fclose($fp);
    }

    public function setSpeed($speed) {
        if (is_numeric($speed) && $speed > 16 && $speed < 4096) $this->_speed = $speed;
    }

    private function getRange($file_size) {
        if (!isset($_SERVER['HTTP_RANGE'])) return null;
        $range = $_SERVER['HTTP_RANGE'];
        // Range: bytes=start-end
        if (preg_match('/bytes=\s*(\d+)\s*-\s*(\d*)/i', $range, $matches)) {
            $start = (int)$matches[1];
            $end = $matches[2] !== '' ? (int)$matches[2] : $file_size - 1;
            if ($start > $end || $start >= $file_size) return null;
            return ['start' => $start, 'end' => min($end, $file_size - 1)];
        }
        return null;
    }
}

使用範例

$downloader = new FileDownload();
$downloader->setSpeed(1024); // 限速 1MB/s
$downloader->download('/data/files/large.zip', 'large.zip', true); // true 啟用續傳

關鍵實作細節

  1. Range 解析:支援 bytes=start- (尾部)、bytes=-end (頭部) 等變體,正則需完整覆蓋。
  2. 邊界檢查:確保 start <= end < file_size,否則回傳 416 Range Not Satisfiable。
  3. 限速機制:配合 fread 區塊大小與 ob_flush() 實現應用層流控;生產環境建議用 Web Server 模組 (Nginx limit_rate) 更高效。
  4. 大檔案記憶體:使用 fread 逐塊輸出,避免 file_get_contents 佔滿記憶體。
  5. 輸出緩衝:需確保 output_buffering 不干擾 ob_flush(),或呼叫 ob_end_clean() 關閉 PHP 緩衝。

進階擴充

  • 多範圍請求:支援 Range: bytes=0-500,600-1000 需回傳 multipart/byteranges,複雜度較高,多數下載工具僅用單範圍。
  • ETag/Last-Modified:配合條件請求實現快取驗證。
  • 加密下載連結:簽名 URL 含過期時間、用戶 ID,防盜鏈。

結語

FileDownload 類別以純 PHP 實現標準 HTTP 斷點續傳協議,核心在於正確解析 Range、回應 206 與 Content-Range、精確 fseek 定位。結合限速、大檔案分塊輸出,即可構建生產級下載服務。

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