當網站提供檔案下載服務時,斷點續傳是提升用戶體驗的關鍵功能。它允許用戶暫停下載,稍後從中斷處繼續,而無需重新下載整個檔案。這對大型檔案或不穩定的網路環境尤為重要。
大多數 Web 伺服器(如 Apache、Nginx)預設支援靜態檔案的斷點續傳。若直接透過 Web 伺服器提供公開靜態檔案下載,通常無需額外配置。然而,這種方式無法讓後端腳本控制下載過程,無法滿足私有檔案、動態生成內容或需權限驗證的場景。
HTTP 協定定義了 Range 標頭,允許客戶端請求資源的特定位元組範圍。例如,對於 1000 位元組的檔案,瀏覽器可請求 bytes=0-299(前 300 位元組)或 bytes=500-999(後 500 位元組)。伺服器回應 206 Partial Content 狀態碼,並附上 Content-Range 標頭指示傳輸範圍。
header('Accept-Ranges: bytes');
告訴瀏覽器此資源支援以位元組為單位的部分傳輸。
$range = "0-".($filesize - 1); // 預設完整檔案
if (isset($_SERVER['HTTP_RANGE'])) {
$range = $_SERVER['HTTP_RANGE']; // 例如 "bytes=500-999"
}
Range 格式可能為:
100-200:第 100 到 200 位元組500-:第 500 位元組到檔案結尾-1000:最後 1000 位元組需驗證:起始位置非負、結束位置大於起始、起始小於檔案長度、結束不超過檔案長度。無效範圍回應 416 Requested Range Not Satisfiable。
header('HTTP/1.1 206 Partial Content');
header('Accept-Ranges: bytes');
header("Content-Range: bytes $start-$end/$filesize");
$length = $end - $start + 1;
header("Content-Length: $length");
注意:Content-Length 是本次傳輸的長度,而非完整檔案大小;狀態碼必須為 206。
$fp = fopen($filepath, 'rb');
fseek($fp, $start);
while (!feof($fp) && $length > 0) {
$buffer = fread($fp, min(8192, $length));
echo $buffer;
$length -= strlen($buffer);
flush();
}
fclose($fp);
ob_clean()),防止輸出前的空白字元損壞檔案。Content-Disposition。斷點續傳本質上是利用 HTTP 協定的部分內容傳輸特性。在 PHP 中實作時,關鍵在於正確處理 Range/Content-Range 標頭、狀態碼 206 以及檔案指標定位。錯誤的標頭會導致下載檔案損壞,務必嚴格遵循 RFC 7233 規範。