You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

本地老版本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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 17:53:17