response::download() 不支持断点续传,因其硬编码返回 200 ok、忽略 range 头、不设 accept-ranges/content-range;应改用 response::stream() 或原生流式处理,正确实现 range 协议解析与响应。

Response::download() 不支持断点续传,别用它
直接调用 Response::download() 实现不了断点续传——它底层硬编码返回 200 OK,强制输出整个文件,完全忽略 Range 请求头,也不设 Accept-Ranges 或 Content-Range。浏览器发来 Range: bytes=5000-,它照样从头吐完整文件,续传必然失败。
常见错误现象:
- 下载中断后重试,进度条回到 0%
- Chrome 控制台报
Failed to load resource: net::ERR_INCOMPLETE_CHUNKED_ENCODING - Wireshark 抓包发现响应始终是
200,且Content-Length是全文件大小
必须绕开它,改用流式响应逻辑。TP6.1+ 推荐用 Response::stream();低于该版本或需精细控制时,直接上原生 PHP 流式处理。
手动实现下载断点续传的四个关键动作
核心不是“写新功能”,而是正确响应 HTTP Range 协议:识别请求区间、计算字节偏移、设对状态码和头、按需输出数据块。
- 先清空所有输出缓冲:
ob_end_clean(),否则fread()输出会被截住 - 禁用 zlib 压缩:
ini_set('zlib.output_compression', 'Off'),不然流式数据被缓存住不实时 - 读取并解析
$_SERVER['HTTP_RANGE'],正则提取起始/结束位置,例如bytes=1024-2047 - 有有效 Range 就返回
206 Partial Content,设Content-Range: bytes 1024-2047/1048576和Accept-Ranges: bytes;无 Range 则退化为完整下载(200+ 全量Content-Length)
示例片段(非完整函数,仅示意关键分支):
if ($range = $_SERVER['HTTP_RANGE'] ?? null) {
preg_match('/bytes=(\d+)-(\d+)?/', $range, $matches);
$start = (int)$matches[1];
$end = $matches[2] ? (int)$matches[2] : $fileSize - 1;
$length = $end - $start + 1;
<pre class="brush:php;toolbar:false;">header('HTTP/1.1 206 Partial Content');
header("Content-Range: bytes {$start}-{$end}/{$fileSize}");
header('Accept-Ranges: bytes');
header("Content-Length: {$length}");} else { header('HTTP/1.1 200 OK'); header("Content-Length: {$fileSize}"); }
中文文件名 + 断点续传的双头设置不能少
只设 filename*=UTF-8''%E6%96%87%E4%BB%B6.pdf,Safari 会拒绝下载;只设 filename="file.pdf",IE/Edge 旧版会乱码。两者必须共存。
-
Content-Disposition必须同时带两个字段:attachment; filename="file.pdf"; filename*=UTF-8''%E6%96%87%E4%BB%B6.pdf - 中文名先转 UTF-8:
mb_convert_encoding($name, 'UTF-8', 'auto'),再rawurlencode()编码 -
filename字段值只能是 basename,不能含路径,否则 Chrome 会截断成空字符串
漏掉任一字段,都会导致某类浏览器无法触发下载,尤其在移动端 Safari 上表现最明显。
Nginx 配置不配好,PHP 写得再对也白搭
PHP 层面逻辑跑通了,Nginx 挡在中间照样让断点续传失效。默认配置下,Nginx 可能丢弃 Range 头、拒绝带下划线的 header、或超时切断长连接。
- 必须开启
underscores_in_headers on;,否则Content-Range被静默丢弃(某些 Nginx 版本默认关闭) -
client_max_body_size不影响断点下载,但fastcgi_read_timeout得调高(建议 ≥300),避免分片传输中途被关连接 - 确认未启用
gzip on或gzip_static on对该接口生效,压缩会破坏流式响应 - 如果用了反向代理,确保 upstream 也透传
Range和Content-Range头
最容易被忽略的是:Nginx 默认不校验 Range 合法性,它只是透传。真正做字节切片、状态判断、头设置的,永远是你的 PHP 代码——别指望 Web 服务器替你干活。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











