php实现大文件断点续传的核心在于正确解析http请求头中的range字段,并据此设置响应的status code为206 partial content、content-range以及正确的content-length。通过fseek定位文件指针并使用readfile或循环输出流,可以高效地传输指定字节范围的数据,从而支持浏览器的断点续传功能。

如何解析Range头部并计算偏移量
当客户端发起带Range头的请求时,服务器需要提取起始和结束字节位置。如果未提供Range头,则视为全量下载请求,返回200状态码;若提供了Range头但格式错误或超出文件范围,则应返回416 Range Not Satisfiable。通常我们只处理bytes=start-end这种标准格式,其中start和end是相对于文件开头的字节索引。

$file = '/path/to/large_file.zip';
$size = filesize($file);
$headers = apache_request_headers();
if (isset($headers['Range'])) {
preg_match('/bytes=(\d+)-(\d*)/', $headers['Range'], $matches);
$start = isset($matches[1]) ? (int)$matches[1] : 0;
$end = isset($matches[2]) && $matches[2] !== '' ? (int)$matches[2] : $size - 1;
if ($start >= $size || $end >= $size) {
header('HTTP/1.1 416 Range Not Satisfiable');
exit;
}
$length = $end - $start + 1;
header('HTTP/1.1 206 Partial Content');
header("Content-Range: bytes $start-$end/$size");
header("Content-Length: $length");
} else {
$start = 0;
$end = $size - 1;
$length = $size;
header('HTTP/1.1 200 OK');
}
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="large_file.zip"');
header('Accept-Ranges: bytes');
$f = fopen($file, 'rb');
fseek($f, $start);
fpassthru($f);
fclose($f);
如何处理并发请求与内存限制
在处理大文件时,必须避免一次性将文件内容加载到内存中。使用fpassthru或分块读取(如fread配合echo)可以确保内存占用恒定。同时,为了支持多个断点同时下载,每个请求都是独立的进程,因此不需要额外的锁机制来保护文件读取,但需注意文件系统本身的并发读写安全性。此外,设置Accept-Ranges: bytes头告知客户端支持按字节范围请求,这是启用断点续传的关键标志。

// 优化版:分块读取以进一步降低内存峰值
set_time_limit(0);
ignore_user_abort(true);
// ... 前面的Header设置代码相同 ...
$bufferSize = 8192; // 8KB buffer
while (!feof($f)) {
echo fread($f, $bufferSize);
flush(); // 强制刷新输出缓冲区
usleep(1000); // 可选:防止CPU占用过高
}
fclose($f);
当Range请求超出文件大小边界时会发生什么
如果客户端请求的Range起始位置大于等于文件总大小,或者结束位置超出文件总大小,服务器应当拒绝该请求并返回416状态码。在实际代码中,我们通过检查$start和$end与$filesize的关系来判断。例如,若文件大小为10MB,客户端请求bytes=10000000-10500000,由于起始位置已等于文件大小,这被视为无效范围,服务器应返回416错误,提示客户端重新协商范围。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











