thinkphp 8.0 的 response::download() 仍不支持断点续传,因其硬编码返回 200 ok、忽略 http_range、不设 accept-ranges 和 content-range,必须手动实现:清空缓冲、禁用 zlib、解析 range、fseek 定位、分块输出。

ThinkPHP 8.0 的 Response::download() 依然不支持断点续传——它硬编码返回 200 OK,忽略 Range 请求头,不设 Accept-Ranges 和 Content-Range,浏览器一发 Range: bytes=5000-,它照样从头吐完整文件。别绕弯子,必须手动接管。
为什么 Response::download() 在 TP8 里还是不能用
TP8 没改底层逻辑:Response::download() 内部仍调用 readfile(),强制清空输出缓冲、设 Content-Length 全量大小、禁用分块响应。它连 $_SERVER['HTTP_RANGE'] 都不读,更别说解析和跳转偏移。常见表现:
- 下载中断后重试,进度条归零,Chrome 控制台报
net::ERR_INCOMPLETE_CHUNKED_ENCODING - Wireshark 抓包看到响应始终是
200,Content-Length是整个文件大小 - 苹果 Safari/iOS 直接拒绝下载(因缺失
Accept-Ranges: bytes)
TP8 中手动实现 Range 下载的四个必做动作
核心不是“写新功能”,而是严格遵循 HTTP/1.1 Range 协议:识别请求区间 → 计算字节偏移 → 设对状态码与头 → 按需输出数据块。TP8 环境下需特别注意:
- 先调用
ob_end_clean()清空所有输出缓冲,否则fread()输出会被截断 - 禁用 zlib 压缩:
ini_set('zlib.output_compression', 'Off'),否则流式数据被缓存住不实时 - 用
fopen($path, 'rb')打开文件,fseek($fp, $start)定位,再循环fread($fp, 8192)+echo+flush()+ob_flush() - 无
Range头时退化为完整下载:返回200 OK,设完整Content-Length和Content-Disposition
中文文件名 + 断点续传的双头设置不能少
只设 filename*=UTF-8''%E6%96%87%E4%BB%B6.pdf,Safari 会拒绝下载;只设 filename="file.pdf",IE/Edge 旧版乱码。两者必须共存,且顺序有讲究:
Content-Disposition: inline; filename="文件.pdf"; filename*=UTF-8''%E6%96%87%E4%BB%B6.pdf-
Content-Type必须设对 MIME 类型,用mime_content_type($path)或finfo_file()获取,别硬写application/octet-stream - 若文件路径来自用户输入,务必用
realpath()+is_file()+is_readable()校验,防路径遍历
TP8 下最容易被忽略的三个坑
很多人在 TP8 里照搬老教程,结果上线就崩:
- Nginx 默认不透传带下划线的 header,必须加
underscores_in_headers on;,否则$_SERVER['HTTP_RANGE']为空 - TP8 的中间件或事件监听器可能提前输出内容(比如日志、调试信息),导致
header()失败,务必在控制器入口第一行就ob_end_clean() - 大文件场景下,
memory_limit和max_execution_time不够会直接 502,建议在方法开头显式设置:ini_set('memory_limit', '512M')、set_time_limit(300)
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











