【问题标题】:How to let users with required permission download a file via php?如何让具有所需权限的用户通过php下载文件?
【发布时间】:2010-06-09 22:21:40
【问题描述】:

我有一个 php 文件,它充当我希望人们下载的所有文件的看门人,他们拥有足够的特权。

我使用的将文件扔给用户的代码是

header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header("Content-disposition: attachment; filename=\"".$public_filename."\""); 
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: ".$f_filesize); 
readfile($file_path);

大多数文件都相当大.... 400mb-10GB。

有什么好的方法可以做到这一点,并保持真实位置+文件名的秘密,这样人们就不能直接链接到文件,而是必须通过我的 download.php?file=ID 网守链接?

谢谢

编辑:我没有问如何进行用户身份验证,所有这些都已完成。我只是问我这样做的方式是否是一个大规模的好主意。如果我继续阅读 10GB 文件,似乎可能会导致内存问题。

【问题讨论】:

    标签: php readfile download


    【解决方案1】:

    好的,让 php 发送大约 400Mb–10Gb 的文件并不好。您需要以某种方式让您使用的任何网络服务器实际提供文件。

    这真的取决于您需要它的安全性。想到的最简单的解决方案(但远不是最安全的)是使用带有长随机名称的符号链接来链接到原始文件。一段时间后,符号链接过期并被删除。每个用户都有自己的符号链接(或“令牌”)到他们正在下载的文件。我不确定这在 Windows 环境中如何发挥作用,但在 unix 上它还是相当简单的。

    这是一些伪代码:

    if($user->isAllowedToDownload($file)){
        $token = md5($user->name . $file->name . time() . $someGoodRandomValue);
        symlink($file, $download_path . $token);
        header("Location: $download_url$token"); 
    }
    

    然后你需要一个清理旧符号链接的 cron 作业。您还需要确保将网络服务器设置为遵循符号链接,最好仅用于创建这些下载令牌的文件夹。

    因此,当用户可能请求domain.com/download?file=bigfile.mp4 时,会在网络服务器公共空间中创建一个符号链接,该链接指向网络服务器公共空间之外的真实文件。用户可能会被重定向到domain.com/getFile/ab739babec890103bdbca72,这反过来又会导致网络服务器提供文件。现在用户很难尝试猜测文件的 URL 是什么,这就是“安全性”。

    【讨论】:

    • 我认为您甚至可以在几秒钟后取消链接文件(此处为链接),我将为当前下载工作。就像您可以创建文件、打开文件、删除文件然后读取文件一样。
    • 不知道,酷!不过,您必须检查用户中止和恢复下载的效果。 (对于这么大的文件,我认为我们可以假设下载会中断?)
    • 您还可以将令牌存储在 cookie 中,并具有将 cookie 转换为文件路径的重写规则。如果未设置 cookie 或符号链接不再存在,您可以使用原始文件名实现漂亮的 URL 并显示 PHP 脚本。
    • 在下载过程中删除符号链接会停止下载,至少对我来说是这样。我正在运行 lighttpd。
    • 为什么说“远非最安全”?我不明白这怎么可能不安全。我错过了什么?
    【解决方案2】:

    您已经这样做了 - $public_filename 是您想要的名称,readfile($file_path) 部分是文件 - 它的位置未公开。除此之外,它可能位于文档根目录之上。

    【讨论】:

    • 我明白.. 但我得到的是.... 让 php 读取 10GB 文件以传递给用户是个好主意吗?
    【解决方案3】:
    1. 将文件放在无法通过 HTTP 访问的位置。
    2. 创建包含文件 ID 和文件路径的数据库表。
    3. 通过文件 ID 链接到文件(如上所述,download.php?fileID=0000)。
    4. ???
    5. 利润。

    作为以前(多年前)这样做的人,您需要考虑这将对您的服务器产生的内存影响。 readfile 函数当时不可用,因此您可能不需要为内存考虑做任何特殊的事情。

    【讨论】:

      【解决方案4】:

      您需要以某种方式对它们进行身份验证(HTML 表单、HTTP basic auth 等),然后设置一个 session 标志,您的 download.php 脚本可以检查该标志。请注意,这不会阻止人们下载文件,然后自行分发。

      您应该配置您的网络服务器,以便无法直接访问真实文件。

      它本身不会导致内存问题。 readfile 不会将文件读入内存。但是,使用 PHP 产生开销。您可以使用X-Sendfile 消除部分延迟。

      【讨论】:

        【解决方案5】:

        您的方法会导致内存问题,但是可以分块读取和输出文件。在 echo 每个文件块之后,您将需要使用 flush() 函数。您还可以更加努力地恢复下载。这仍然是一种消耗 CPU 的方法。

        更简单更好的解决方案是通过它们的模块使用 apache 和 lighttpd 都支持的“x-sendfile”标头标签。您所要做的就是在标题中指定文件名,类似于:

        header('X-Sendfile: filename-on-your-file-system');
        

        lighttpd 的链接:

        http://redmine.lighttpd.net/projects/lighttpd/wiki/X-LIGHTTPD-send-file

        【讨论】:

          猜你喜欢
          • 2021-09-01
          • 1970-01-01
          • 2019-03-11
          • 1970-01-01
          • 1970-01-01
          • 2012-02-15
          • 2012-07-30
          • 2017-03-01
          • 1970-01-01
          相关资源
          最近更新 更多