使用 Apache mod_header 的动态文件名和使用 Content-Disposition 的文件下载
我的网站上遇到一个严重问题,当我使用 PHP 将 Content-Disposition 设置为“attachment”,然后使用 readfile() 发送文件时,我的文件下载被截断。我知道这是一个臭名昭著的问题,因为 PHP 的 readfile()
手册页上对此有很多讨论。我已经尝试了那里发布的每个解决方案,但没有成功(发送分块输出,在 Apache/PHP 中禁用 gzip 压缩)。下面是该代码的要点,没有所有的解决方法,就像您在 PHP 手册中找到的那样:
$file = '/path/to/ugly.file.name.mp4';
$filename = 'ANiceFileName.mp4';
header('Content-Description: File Transfer');
header('Content-Type: video/mp4');
header('Content-Disposition: attachment; filename='.$filename.'.mp4');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
最重要的是,请注意,我希望能够向浏览器发送动态文件名,以避免我使用更多机器友好的代码将文件存储在磁盘上。是的,在这段代码执行之前我已经做了很多安全检查——我已经知道 readfile() 可能是一个安全缺陷。 :-)
我发现发送完整文件的唯一可靠方法是简单地丢弃 PHP 并使用 Apache 的 mod_header
强制下载该文件。就像这样:
<FilesMatch "\.mp4$">
ForceType application/octet-stream
Header set Content-Disposition attachment
</FilesMatch>
这工作得很好,但是有一个大问题——我不能再根据请求生成动态文件名,这对我的内容来说有点重要。我已经知道如何指定 ;filename=file_name.mp4
,但请记住 - 我需要指定一个动态名称,而不仅仅是 file_name.mp4
。
如果我能在发送文件时以某种方式通知 Apache(使用 PHP)动态文件名,那就太好了。这可能吗?或者我是否将被迫将磁盘上的所有文件重命名为用户友好的名称? PHP 解决方案根本不起作用。我的文件有时高达 15MB,而在传输时这些文件通常会被截断为不到 2MB。哎哟!
帮助我 Stack Overflow Kenobi!你是我唯一的希望。
I have a bad problem on my site where my file downloads are getting truncated when I use PHP to set the Content-Disposition to "attachment" and then sending the file using readfile()
. I know this is somewhat of a notorious problem since there is plenty of discussion about it on PHP's readfile()
manual page. I have tried each of the solutions posted there with no luck (sending chunked output, disabling gzip compression in both Apache/PHP). Here is the gist of that code, without all the workarounds, just as you'd find it on the PHP manual:
$file = '/path/to/ugly.file.name.mp4';
$filename = 'ANiceFileName.mp4';
header('Content-Description: File Transfer');
header('Content-Type: video/mp4');
header('Content-Disposition: attachment; filename='.$filename.'.mp4');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
Most importantly, notice that I'd like to be able to send a dynamic file name to the browser, to avoid the more machine-friendly codes I use to store the files on disk. And yes, I have done plenty of security checks before this bit of code executes -- I already know readfile()
can be a security flaw. :-)
The only reliable way I have found to send a complete file is to simply trash PHP and instead use Apache's mod_header
to force the file as a download. Like so:
<FilesMatch "\.mp4$">
ForceType application/octet-stream
Header set Content-Disposition attachment
</FilesMatch>
This works very well, but there's a big problem -- I can no longer generate dynamic file names upon request, which is somewhat important to my content. I already know about specifying ;filename=file_name.mp4
, but remember - I need to specify a dynamic name rather than just file_name.mp4
.
It would be really nice if I could somehow notify Apache (using PHP) of the dynamic file name when sending a file. Is that possible? Or am I going to be forced to rename all my files on disk to a user-friendly name? The PHP solutions just aren't working at all. My files are sometimes up to 15MB, and those are often truncated to less than 2MB when transferring. Ouch!
Help me Stack Overflow Kenobi! You're my only hope.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
按照此处。这基本上允许 PHP 在 PHP 设置 X-Sendfile 标头时退出 HTTP 会话的中间 - 但是,正如您在该博客上看到的,它允许设置您的 Content-Disposition 标头。
Try mod_xsendfile as described here. This basically allows PHP to step out of the middle of the HTTP conversation at the point that PHP sets the X-Sendfile header - but, as you can see on that blog, it allows for setting your Content-Disposition header.
是否因为 php 脚本超时而被截断?设置时间限制?
Is it getting truncated because the php script times out? set_time_limit?