使用download.php下载文件

问题描述 投票:3回答:5

我需要向客户提供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方面)?

  • 1:使用application/download的PHP解决方案 header('Content-Type: application/download'); header('Content-Disposition: attachment; filename=file.zip'); readfile("/path/to/file.zip");
  • 1bis:使用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");
  • 1ter:使用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");
  • 1quater:与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");
  • 2:Apache解决方案,不涉及PHP:让Apache提供文件,并使用.htaccess为同一个文件提供不同的URL(可以编写很多方法)。在性能方面,它类似于让客户下载由Apache服务器提供服务的example.com/file.zip
  • 3:另一个PHP解决方案。这可能会奏效: $myfile = file_get_contents("file.zip"); echo $myfile; 但这不会要求PHP将整个内容加载到内存中吗? (这在性能方面会很糟糕)

注意:readfile doc说:

即使在发送大文件时,readfile()也不会出现任何内存问题。如果遇到内存不足错误,请确保使用ob_get_level()关闭输出缓冲。

但我想100%确定它不会比纯Apache解决方案更慢/更多CPU / RAM饥饿。

php apache .htaccess download
5个回答
5
投票

您可以使用.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时,将开始下载并关闭连接。

如果用户访问了无效的网址,他们将被重定向到主页。


3
投票

我会和readfile一起去。我使用它多年,并且从未遇到内存问题,甚至在128MB VPS上运行。

使用PHP意味着您可以轻松处理身份验证,授权,日志记录,添加和删除用户,使URL过期等。你可以使用.htaccess来做到这一点,但你必须编写一个相当大的结构来处理这个问题。


2
投票

您将面对这些尺寸的文件遇到的最大问题如下:

  • 人们用下载管理器下载它
  • 中断连接

通常情况下,keep-alive可能是一个坏主意,因为它专门用于下载连接,这可能会阻碍您的网络连接,而不是允许它们轻松释放。但是,如果您希望所有文件都很大,那么这就是您的朋友,因为您不希望人们重新启动这些下载。这些下载将与keep-alive建立可靠的连接,并使客户端更容易恢复,这有助于减少尝试重新下载大量文件的人。

因此,我推荐您提供的选项

1ter

但是,和其他人一样,我仍然建议您测试您的解决方案,最好是从您提供文件的地方以外的位置进行测试。

附录:这说,除非你必须获得头控制功能和.htaccess控制,否则使用PHP并不是最好的主意,因为它只是增加了更多的处理能力。到目前为止,更好的路径就是将文件放在可访问的目录中。 .htaccess可以重写对文件和文件夹的访问权限,而不仅仅是PHP脚本。

要改为创建基于Apache的受保护下载文件夹:

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登录。

NOTICE:

现在,这就是说,人们可以访问文件,共享完整链接(用户名/密码)。因此,它们与服务器的https(或http,如果允许)协议以及用户共享或不共享链接一样安全(或不安全)。

通过这种方式,文件将向用户开放,具有Web可访问的全部功能,这意味着下载帮助程序,帮助的浏览器插件,REST调用等等,具体取决于用户的用例。这可能会降低安全性,这取决于您托管的内容,这可能是也可能不是什么大不了的事。如果您正在托管私人医疗数据(少数用户,高安全性,低速要求),我不会这样做。如果您正在托管音乐专辑,我会完全这样做(许多用户,低安全性,高速要求)。


0
投票

当您的网络服务器是Nginx时,您可以使用X-Accel-Redirect。对于Apache,它是带有X-Sendfile头的mod_xsendfile。

<?php
header('X-Accel-Redirect: /download/f6zDaq/file.zip');

它的成本更低,性能也更好,因为Web服务器处理文件。


0
投票

内存和CPU明智的你应该使用readfile()或使用fopen()fread()自定义缓冲区大小编写一些自定义代码。

关于您发送的标头,它们不会影响脚本的性能,它们只会指示客户端如何处理服务器响应(在您的情况下,文件)。你可以谷歌每个标题,看看它究竟是做什么的。

你可能应该看看这个:Is there a good implementation of partial file downloading in PHP?。那里可能有趣的事情:下载范围和下载恢复支持,使用Web服务器插件,PEAR包或提供所需功能的库的方法。

© www.soinside.com 2019 - 2024. All rights reserved.