我需要向客户提供file.zip
(~2 GB)等大文件,并为每个客户提供唯一的URL。然后我将重定向(与.htaccess
)客户下载链接example.com/download/f6zDaq/file.zip
类似的东西
example.com/download.php?id=f6zDaq&file=file.zip
但由于文件很大,我不希望PHP处理下载(而不仅仅是让Apache处理它)成为我服务器的CPU / RAM性能问题。毕竟,要求PHP执行此操作涉及一个新层,因此如果操作不当,可能会导致此类问题。
问题:在以下解决方案中,哪一个是最佳实践? (特别是在CPU / RAM方面)?
application/download
的PHP解决方案
header('Content-Type: application/download');
header('Content-Disposition: attachment; filename=file.zip');
readfile("/path/to/file.zip");
application/octet-stream
的PHP解决方案(来自this page的示例#1)
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");
application/octet-stream
的PHP解决方案(来自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");
application/force-download
的另一个PHP变体(编辑;来自here):
header("Content-Disposition: attachment; filename=file.zip");
header("Content-Type: application/force-download");
header("Content-Length: " . filesize($file));
header("Connection: close");
example.com/file.zip
。$myfile = file_get_contents("file.zip");
echo $myfile;
但这不会要求PHP将整个内容加载到内存中吗? (这在性能方面会很糟糕)注意:readfile doc说:
即使在发送大文件时,readfile()也不会出现任何内存问题。如果遇到内存不足错误,请确保使用ob_get_level()关闭输出缓冲。
但我想100%确定它不会比纯Apache解决方案更慢/更多CPU / RAM饥饿。
您可以使用.htaccess
将请求重定向到文件,同时保持永久链接结构:
RewriteEngine On
RewriteBase /
RewriteRule ^download\/([^\/]+)\/file.zip download.php?id=$1 [L,NC]
然后在你的download.php
中,你可以检查提供的id是否有效:
// Path to file
$file = 'file.zip';
// If the ID is valid
if ($condition) {
header("Content-Disposition: attachment; filename=\"" . basename($file) . "\"");
header("Content-Type: application/force-download");
header("Content-Length: " . filesize($file));
header("Connection: close");
} else {
// Handle invalid ids
header('Location: /');
}
当用户访问有效的网址http://example.com/download/f6zDaq/file.zip
时,将开始下载并关闭连接。
如果用户访问了无效的网址,他们将被重定向到主页。
我会和readfile
一起去。我使用它多年,并且从未遇到内存问题,甚至在128MB VPS上运行。
使用PHP意味着您可以轻松处理身份验证,授权,日志记录,添加和删除用户,使URL过期等。你可以使用.htaccess
来做到这一点,但你必须编写一个相当大的结构来处理这个问题。
您将面对这些尺寸的文件遇到的最大问题如下:
通常情况下,keep-alive可能是一个坏主意,因为它专门用于下载连接,这可能会阻碍您的网络连接,而不是允许它们轻松释放。但是,如果您希望所有文件都很大,那么这就是您的朋友,因为您不希望人们重新启动这些下载。这些下载将与keep-alive建立可靠的连接,并使客户端更容易恢复,这有助于减少尝试重新下载大量文件的人。
因此,我推荐您提供的选项
但是,和其他人一样,我仍然建议您测试您的解决方案,最好是从您提供文件的地方以外的位置进行测试。
附录:这说,除非你必须获得头控制功能和.htaccess控制,否则使用PHP并不是最好的主意,因为它只是增加了更多的处理能力。到目前为止,更好的路径就是将文件放在可访问的目录中。 .htaccess可以重写对文件和文件夹的访问权限,而不仅仅是PHP脚本。
Options +FollowSymLinks
RewriteEngine On
RewriteRule ^/user/files/folder1.*$ http://example.com/userfiles/ [R=301,L]
然后,如果您需要对其进行密码保护,而不是使用PHP,请使用Apache(已经安装了大多数PHP安装)。您可以通过在目标文件夹中包含.htaccess文件来实现此目的(如果您正在动态创建用户,则可能需要创建一个脚本来为每个新用户生成这些文件),并确保apache已准备好处理密码:
AuthType Basic
AuthName "Authentication Required"
AuthUserFile "/user/password/.htpasswd"
Require valid-user
(详情请见:Setting up Apache Passwords)
在此之后,确保密码目录中的.htpasswd文件的格式为username:password / hashedpassword。
e.f.:
andreas:$apr1$dHjB0/..$mkTTbqwpK/0h/rz4ZeN8M0
john:$apr1$IHaD0/..$N9ne/Bqnh8.MyOtvKU56j1
现在,假设您不希望他们每次都传递密码,请在下载链接中包含访问权限
<a href="user:pass@http://example.com/userfiles/myCoolZip.zip">Link (hopefully behind a password-protected interface.)</a>
[注意:如果没有为每个文件随机分配密码,请不要使用直接密码链接方法。]
或者如果您根据root apache密码管理进行填充并且您的站点正在使用apache进行登录过程,则他们可能不需要用户:完全传递部分链接,已经使用Apache登录。
现在,这就是说,人们可以访问文件,共享完整链接(用户名/密码)。因此,它们与服务器的https(或http,如果允许)协议以及用户共享或不共享链接一样安全(或不安全)。
通过这种方式,文件将向用户开放,具有Web可访问的全部功能,这意味着下载帮助程序,帮助的浏览器插件,REST调用等等,具体取决于用户的用例。这可能会降低安全性,这取决于您托管的内容,这可能是也可能不是什么大不了的事。如果您正在托管私人医疗数据(少数用户,高安全性,低速要求),我不会这样做。如果您正在托管音乐专辑,我会完全这样做(许多用户,低安全性,高速要求)。
当您的网络服务器是Nginx时,您可以使用X-Accel-Redirect
。对于Apache,它是带有X-Sendfile
头的mod_xsendfile。
<?php
header('X-Accel-Redirect: /download/f6zDaq/file.zip');
它的成本更低,性能也更好,因为Web服务器处理文件。
内存和CPU明智的你应该使用readfile()
或使用fopen()
和fread()
自定义缓冲区大小编写一些自定义代码。
关于您发送的标头,它们不会影响脚本的性能,它们只会指示客户端如何处理服务器响应(在您的情况下,文件)。你可以谷歌每个标题,看看它究竟是做什么的。
你可能应该看看这个:Is there a good implementation of partial file downloading in PHP?。那里可能有趣的事情:下载范围和下载恢复支持,使用Web服务器插件,PEAR包或提供所需功能的库的方法。