在本地主机上运行 PHP/Apache。是的,非常旧的版本(Apache/2.4.10 (Win32) PHP/5.4.34),但代码已经工作了很多年。我一直依赖 Xsendfile Apache 模块,但它默默地失败了。更改为使用对 header() 的显式调用并回显内容。这会导致 Chrome 显示“无法完成下载”。 Firefox 表示“无法保存 example.zip.part,因为无法读取源文件。 请稍后重试,或联系服务器管理员。”
Windows 10 上的 Chrome 117.0.5938.89(官方版本)(64 位)、Firefox 117.0.1(64 位)。
这是代码:
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;
}
我只是将“false &&”放入“if”中以强制它不使用Xsendfile模块。而且,正如我所说,这改变了 Chrome 中的无提示失败(UI 中没有任何内容表明已尝试下载)的行为,到下载开始(显示 0/32.1 KB,还剩 38 秒),然后最后“无法完成下载”。
我完全被难住了!相同的代码之前曾在我的 PC 上运行过,下载相同的文件(使用 Xsendfile 模块)。我通常传入 $attachment = true;但也尝试了 $attachment = false,没有任何改进。
正如评论所指出的,编译指示和缓存控制内容只是从某个帖子中复制的。过去 12 年左右,它一直在公共网站上运行......
编辑: FWIW,我已经尝试了另外两件事,我看到过关于下载问题的帖子:
顺便说一句,我正在运行 ESET Internet Security 16.2.13.0,但我已禁用其防火墙,并关闭了这些测试的病毒扫描。但同样,结果没有改变......
*** 更新*** 我还尝试删除 Pragma:、Expires: 和 Cache-control: 标头,同样没有任何效果。
我能够下载这个小 (37KB) zip 文件的唯一方法是使其成为也具有 download 属性的元素的 href 属性。所以我想我会尝试做一些能做到这一点的东西。当前的代码很好,因为在用户要求下载之前我不必创建 zip 文件,并且不会向用户透露来自 Web 根目录的路径。对于静态元素,我必须使 zip 文件与用户所做的任何更改保持最新,并且用户可以看到该文件的路径。尽管我可以使用 javascript 动态创建元素并触发对其的单击,从而允许我仅在请求时创建 zip 文件,并对除了高度好奇的用户之外的所有用户隐藏路径。多么令人难以置信的痛苦!
这并不能完全回答问题,因为除了“Chrome 和 Firefox 最近发生了一些变化”之外,我找不到任何对该行为的真正解释。相反,它只是说明了我解决问题的方式,这阻碍了我的发展。虽然这个问题没有引起太大的反响,但我已经看到了足够多的关于在 PHP 中下载文件的其他老问题,相信其他人会在某个时候遇到这个问题......
我没有使用指向服务器上使用 PHP 下载文件的操作例程的 href 的锚点,而是将 href 更改为 javascript: 协议,该协议调用一个函数来动态生成带有文件 url 的锚点,以及download 属性指定下载文件的所需名称。然后触发对该锚点的点击。有趣的是,甚至不需要将新锚点插入到 DOM 中,我只需创建它并单击它,Chrome 就会显示一些东西飞入下载文件夹的小动画 - 当我可以检查时,下载已经完成。与 Firefox 类似。
<script type="text/javascript">
downloadzip = function(url) {
var anchor = document.createElement('a');
anchor.href = url;
anchor.target = '_blank';
anchor.download = 'uploads.zip';
anchor.click();
}
</script>
<a href="javascript:downloadzip('<?php echo $this->download_uri;?>');">Download the zip file</a>