问题描述
我需要向客户提供诸如 file.zip
(~2 GB) 之类的大文件,并为每个客户提供一个唯一的 URL.然后我将(使用 .htaccess
)重定向客户下载链接 example.com/download/f6zDaq/file.zip
到类似
I need to deliver big files like file.zip
(~2 GB) to customers, with a unique URL for each customer. Then I will redirect (with .htaccess
) a customer download link example.com/download/f6zDaq/file.zip
to something like
example.com/download.php?id=f6zDaq&file=file.zip
但由于文件很大,我不希望 PHP 处理下载(而不仅仅是让 Apache 处理它)成为我服务器的 CPU/RAM 性能问题.毕竟,让 PHP 来做这件事涉及到一个新层,所以如果做得不好,它可能会导致这样的问题.
But as the files are big, I don't want the fact that PHP processes the downloading (instead of just letting Apache handle it) to be a CPU / RAM performance issue for my server. After all, asking PHP to do it involves a new layer, so it might cause such an issue, if not done properly.
问题:在以下解决方案中,哪些是最佳实践?(特别是在 CPU/RAM 方面)?
Question: among the following solutions, which one(s) are the best practice? (in particular, in terms of CPU/RAM)?
1:带有
application/download
header('Content-Type: application/download');
header('Content-Disposition: attachment; filename=file.zip');
readfile("/path/to/file.zip");
下载时测得的 CPU 使用率:13.6%.
CPU usage measured while downloading: 13.6%.
1bis:带有 application/octet-stream
的 PHP 解决方案(来自 本页)
1bis: PHP solution with application/octet-stream
(coming from Example #1 of this page)
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=file.zip');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize('file.zip'));
readfile("/path/to/file.zip");
1ter:带有 application/octet-stream
的 PHP 解决方案(来自此处):
1ter: PHP solution with application/octet-stream
(coming from here):
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=file.zip');
header('Content-Transfer-Encoding: binary'); // additional line
header('Connection: Keep-Alive');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); // additional line
header('Pragma: public');
header('Content-Length: ' . filesize('file.zip'));
readfile("/path/to/file.zip");
1quater:另一个带有 application/force-download
的 PHP 变体(已编辑;来自此处a>):
1quater: Another PHP variant with application/force-download
(edited; coming from here):
header("Content-Disposition: attachment; filename=file.zip");
header("Content-Type: application/force-download");
header("Content-Length: " . filesize($file));
header("Connection: close");
3: Another PHP solution. This would probably work:
$myfile = file_get_contents("file.zip");
echo $myfile;
但这不会要求 PHP 将整个内容加载到内存中吗?(这在性能方面会很糟糕!)
4:只需执行 header("Location:/abcd/file.zip");
重定向,如 使用原始文件名下载的带有短网址的文件.
4: Just do a header("Location: /abcd/file.zip");
redirection as explained in File with a short URL downloaded with original filename.
此解决方案的问题:这会公开文件的实际位置
Problem with this solution: this discloses the actual location of the file
example.com/abcd/file.zip
对于不需要的最终用户(然后他们可以使用或共享此 URL 无需身份验证)...
to the end user (who can then use or share this URL without authentification) which is not wanted...
但另一方面,它对 CPU 来说要轻得多,因为 PHP 只是重定向请求而不是传递文件本身.
But on the other hand, it is much lighter for the CPU since PHP just redirects the request and doesn't deliver the file itself.
下载时测得的 CPU 使用率:10.6%.
CPU usage measured while downloading: 10.6%.
注意:readfile 文档说:
readfile() 本身不会出现任何内存问题,即使在发送大文件时也是如此.如果遇到内存不足错误,请确保使用 ob_get_level() 关闭输出缓冲.
但我想 100% 确定它不会比纯 Apache 解决方案更慢/更多 CPU/RAM 需求.
推荐答案
如 使用 PHP 提供文件的最快方法,我终于做到了:
apt-get install libapache2-mod-xsendfile
a2enmod xsendfile # (should be already done by previous line)
然后我在 apache2.conf
中添加了这个:
Then I added this in apache2.conf
:
<Directory />
AllowOverride All
Require all granted
XSendFile on
XSendFilePath /home/www/example.com/files/
</Directory>
然后我做了 service apache2 restart
并将其包含在 .htaccess
中:
I then did service apache2 restart
and included this in .htaccess
:
RewriteRule ^(.*)$ download.php?file=$1 [L,QSA]
在 download.php
中:
header("X-Sendfile: /home/www/example.com/files/hiddenfolder_w33vbr0upk80/" . $file);
header("Content-type: application/octet-stream");
header('Content-Disposition: attachment; filename="' . $file . '"');
注意:奇怪的是,即使我在 apache2.conf
VirtualHost 中启用了 AllowOverride All
,这样做:
NB: strangely, even I have AllowOverride All
enabled in the apache2.conf
VirtualHost, doing this:
XSendFile on
XSendFilePath /home/www/example.com/files/
只是在 /home/www/example.com/.htaccess
或 /home/www/example.com/files/.htaccess
文件中不起作用(它因 xsendFilePath not allowed here
而失败).
just in the /home/www/example.com/.htaccess
or /home/www/example.com/files/.htaccess
file didn't work (it fails with xsendFilePath not allowed here
).
基准:
- 下载时 CPU 为 10.6%,就像我使用 Apache 直接下载文件一样(完全没有 PHP),所以一切都很好!
这篇关于使用 download.php 下载文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!