PHP下载文件断点续传怎么实现_PHP大文件下载支持技巧【方法】

10次阅读

PHP 下载文件断点续传怎么实现_PHP 大文件下载支持技巧【方法】

PHP 如何用 header() 开启断点续传支持

断点续传不是靠 PHP 自己“记住进度”,而是靠 HTTP 协议配合客户端(比如浏览器、wget、curl)重新发起带 Range 头的请求。PHP 要做的是正确响应这个头,并返回对应字节段——核心就是设置 Accept-Ranges: bytes 和处理 Range 请求头。

常见错误是只加了 Content-Range 但没设 Accept-Ranges,导致客户端压根不发Range 请求;或者忽略 If-Range 校验,让过期缓存继续续传出错数据。

  • 必须在输出文件内容前调用header('Accept-Ranges: bytes')
  • 检查 $_SERVER['HTTP_RANGE'] 是否存在,格式是否为bytes=N-M,否则按完整文件响应
  • fopen() 打开文件后,用 fseek() 跳转到起始位置,再用 fread() 读取指定长度,别用 file_get_contents() 全载入内存
  • 响应状态码要分情况:206 Partial Content(有 Range),200 OK(无 Range),不能一律用 200

为什么 fseek() + fread()readfile()更安全

readfile()会把整个文件一次性刷进输出缓冲区,对大文件(比如 2GB 视频)极易触发内存溢出或超时;而 fseek()+fread() 可以按块读取、边读边输出,内存占用稳定在几 KB。

另外 readfile() 无法精确控制起始偏移和长度,没法适配 Range 请求;哪怕你手动截断输出,HTTP 头里的 Content-Length 也容易算错,导致客户端解析失败。

立即学习PHP 免费学习笔记(深入)”;

  • fopen($file, 'rb') 以二进制模式打开,避免 Windows 下换行符干扰
  • 计算 Content-Length 时,用$end - $start + 1,不是$end - $start
  • 读取循环中每次 fread() 不超过 8192 字节,防止阻塞太久被 Nginx/Apache 切断连接
  • 记得 ob_end_clean() 清空已有输出缓冲,否则 header() 会失败

Apache/Nginx 下 mod_xsendfileX-Accel-Redirect能替代 PHP 逻辑吗

能,而且更推荐。PHP 只负责校验权限、生成重定向头,真实文件传输交给 Web 服务器,既省 PHP 资源,又天然支持断点续传、gzip 压缩、缓存协商。

但要注意:启用后 PHP 不能再输出任何内容(包括空格、BOM),否则重定向失效;且路径需映射到 Web 服务器可访问的内部位置,不能暴露真实磁盘路径。

  • Apache 需启用 mod_xsendfile,并配置XSendFile OnXSendFilePath /path/to/files
  • Nginx 用 X-Accel-Redirect 时,需在 location 里配置internal,并确保响应头中的路径匹配内部别名
  • PHP 中只需header('X-Sendfile: /real/path/to/file.zip')(Apache)或header('X-Accel-Redirect: /nginx-alias/file.zip')(Nginx)
  • 别忘了仍要设置Accept-Ranges: bytes——Web 服务器会自动处理 Range,但前提是这个头存在

客户端不发 Range 请求?检查这几个地方

即使 PHP 代码完全正确,客户端也可能因为缓存策略、HTTP 版本或工具限制,始终发完整请求。这不是 PHP 的问题,但排查时容易误判。

典型表现:用 wget -c 下载失败,Chrome 开发者工具 Network 面板里看不到 Range 请求头,curl -H "Range: bytes=0-1023" -I返回 200 而非 206。

  • 确认客户端 HTTP 版本≥1.1(HTTP/1.0 不支持 Range)
  • 检查响应头是否有 Cache-Control: no-storePragma: no-cache——某些旧客户端遇到强禁止缓存时,会放弃续传
  • curl -v -H "Range: bytes=0-1023" http://yoursite/file.zip 直连测试,排除 CDN 或反向代理拦截 Range 头
  • 部分 CDN(如 Cloudflare 免费版)默认剥离 Range 头,需在页面规则里显式开启“Partial Request”支持

断点续传真正难的不是 PHP 怎么写,而是理清谁在哪个环节控制哪段字节——客户端决定要哪块,Web 服务器决定能不能给,PHP 只是中间那个校验权限、拼好头、不搞错偏移量的角色。漏掉 Accept-Ranges 头、用错读取方式、或者被 CDN 静默过滤 Range,都会让整套逻辑失效。

text=ZqhQzanResources