本地老版本Apache/PHP文件下载代码失效,Chrome/Firefox均报错
旧环境下PHP文件下载失效问题解决
环境与问题现象
- 服务器环境:Apache/2.4.10(Win32)、PHP/5.4.34
- 原依赖
mod_xsendfile模块的下载代码静默失败,强制禁用后改用header()+输出文件内容的方式,出现浏览器报错:- Chrome:提示“无法完成下载”
- Firefox:提示“example.zip.part无法保存,源文件无法读取”
- 浏览器版本:Windows 10 下 Chrome 117.0.5938.89(64位)、Firefox 117.0.1(64位)
- 当前仅带
download属性的a标签可下载,但存在文件时效性维护、路径暴露问题,需代码层面解决方案
原失效代码
if (false && in_array('mod_xsendfile', apache_get_modules())) { // Despite the documentation in https://tn123.org/mod_xsendfile/beta/, simply encoding // the pathname with urlencode causes X-Sendfile to provoke an internal server error. // See http://stackoverflow.com/questions/26769144/what-encoding-does-xsendfile-expect $encoded = str_replace('%2F', '/', rawurlencode($pathname)); header("Content-Type: $mime_info"); if ($attachment === true) { $dispo = 'attachment'; } else { $dispo = 'inline'; } header("Content-Disposition: $dispo; filename=\"$basename\""); // xsendfile makes apache generate headers and body for best download behavior. header("X-SendFile: $encoded"); } else { // At least one post somewhere said these were good headers for manual download header("Pragma: public"); header("Expires: 0"); header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); header("Cache-Control: private", false); // required for certain browsers header("Content-Type: $mime_info"); if ($attachment === true) { header("Content-Disposition: attachment; filename=\"$basename\""); } header("Content-Length: " . filesize("$pathname")); ob_clean(); flush(); $data = file_get_contents("$pathname"); echo $data; }
已尝试无效的操作
- 切换
$attachment为true/false - 使用浏览器无痕模式
- 添加
Content-Transfer-Encoding头 - 移除缓存相关头
- 关闭ESET防火墙和病毒扫描
代码层面解决方案
方案1:修复文件读取与输出方式(替换一次性读取为分段输出)
旧代码中file_get_contents一次性读取文件到内存,易因输出缓冲、内存限制或文件权限问题导致传输中断,改用分段读取输出:
else { header("Content-Type: $mime_info"); $dispo = $attachment ? 'attachment' : 'inline'; header("Content-Disposition: $dispo; filename=\"$basename\""); header("Content-Length: " . filesize($pathname)); // 彻底清理所有输出缓冲 while (ob_get_level()) { ob_end_clean(); } flush(); // 分段读取文件并输出 $handle = fopen($pathname, 'rb'); if ($handle) { while (!feof($handle)) { echo fread($handle, 4096); flush(); } fclose($handle); } exit; // 确保脚本后续无额外输出 }
方案2:修复文件名编码(兼容旧浏览器)
旧PHP版本处理特殊字符/中文文件名时易出错,对文件名做双编码兼容:
// 替换原Content-Disposition头代码 $encoded_filename = rawurlencode($basename); header("Content-Disposition: $dispo; filename=\"$encoded_filename\"; filename*=UTF-8''$encoded_filename");
方案3:检查文件路径与权限
- 确保
$pathname为绝对路径,避免相对路径解析错误 - 验证Apache/PHP运行用户对目标文件有读取权限(Windows下检查文件安全选项卡,添加对应用户的读取权限)
方案4:禁用Apache的mod_deflate压缩
若服务器开启mod_deflate,会导致Content-Length与实际输出不匹配,可在脚本中添加:
header('Content-Encoding: none');
或在Apache配置中对下载文件禁用压缩:
<FilesMatch "\.(zip)$"> SetEnv no-gzip 1 </FilesMatch>
内容的提问来源于stack exchange,提问作者sootsnoot
相关产品推荐
相关产品推荐

