tp6文件下载需分场景:小文件用readfile()配正确content-type与utf-8文件名;大文件用fopen()+fpassthru()流式输出;断点续传需校验range头、返回206状态及content-range;全程注重路径白名单、权限校验与限速防护。

TP6 实现文件下载接口,核心在于正确设置响应头、控制输出流,并兼顾大小文件与断点续传等实际需求。不写死路径、不忽略 MIME 类型、不跳过 Range 头校验——这几个细节决定下载是否稳定可用。
基础文件下载(小文件适用)
适合 PDF、TXT、Excel 等几 MB 以内的静态文件。
- 用
readfile()直接输出,避免内存加载全文件; - 必须设置
Content-Type(用mime_content_type()获取更准,别硬编码); -
Content-Disposition中的文件名需做 UTF-8 编码处理,否则中文名在 Chrome/Firefox 下可能乱码或截断; - 加上
Accept-Ranges: bytes头,为后续支持断点续传留余地。
大文件流式下载(规避内存溢出)
超过 50MB 的文件,禁用 file_get_contents() 或一次性读入内存的方式。
- 用
fopen() + fpassthru()组合,边读边输出; - 设置
ob_end_clean()和flush()防止输出缓冲干扰; - 添加
Connection: close头,明确告知连接将关闭,避免 Keep-Alive 导致客户端等待超时; - 若文件来自远程 URL(如 OSS、CDN),优先用
cURL流式转发,而非先下载到本地再吐出。
支持断点续传的下载(网盘类场景)
用户暂停后能继续,对网盘、更新包分发等场景关键。
- 第一步不是写 PHP,而是用
curl -I -H "Range: bytes=0-999" [url]验证源链接是否返回206 Partial Content和Accept-Ranges: bytes; - 百度网盘分享链接默认不支持 Range,必须先解析出真实 CDN 地址(如用
baidu-wangpan-parse工具); - 在控制器中通过
$this->request->header('range')获取请求范围,正则提取起始/结束字节; - 计算好
Content-Range、Content-Length和状态码(206),再用fseek() + fread()精确读取对应段落。
安全与健壮性补充
生产环境不能只管“能下”,还要防错、防盗、防滥用。
- 文件路径必须白名单校验或使用相对存储目录(如
public/uploads/),禁止用户通过参数穿透到系统路径; - 对敏感文件加权限判断(如登录态、文件所属关系),不要仅靠 URL 隐藏;
- 下载前记录日志(IP、文件名、时间),便于溯源异常高频请求;
- 大文件下载建议加限速(如每秒 512KB),防止带宽打满影响其他接口。











