【问题标题】:Any downsides to using header("Location:...") for file downloads?使用 header("Location:...") 下载文件有什么缺点吗?
【发布时间】:2014-10-31 13:31:43
【问题描述】:

我正在我的网站上设置 MP3 下载,并希望人们单击电子邮件中的链接以下载 MP3 的 ZIP 文件。我希望他们点击的 URL 不直接指向文件,这样我就不会透露实际位置——我还可以为下载等添加时间限制。

所以我一直在使用 readfile($file_url) 并尝试了很多 header() 选项,但并没有走得太远。然后我读到你可以使用:

header("Location: " . $file_url);

这在我测试过的浏览器中似乎可以正常工作。与 readfile($file_url) 相比,这样做有什么缺点吗?

谢谢!

【问题讨论】:

    标签: php zip download


    【解决方案1】:

    “缺点”取决于您要完成的工作。

    通过设置 Location 标头,您将发送重定向。实际上,浏览器将收到带有 HTTP 状态代码 302(“已找到”)和资源的新 URL 的响应。基本上,您将它们重定向到另一个资源。
    为了使其正常工作,您将它们重定向到的资源必须是可公开访问的。也就是说:它必须是服务器“文档根目录”中的现有文档。

    此方法具有某些特征,根据您的要求,可能被视为缺点:

    1. 如上所述,资源必须可公开访问。因此,您不能将 MP3 文件放在单独的、不可访问的文件夹中。实际上,假设您的文档根目录位于 /var/www/public,您可能希望将这些文件放在不可访问的文件夹中,例如 /var/www/resources,并仅通过您的 PHP 脚本提供它们。
    2. 您的用户将收到资源的 URL,因此他们可以直接访问该资源,而无需通过您的 PHP 脚本。

    如果这些“特性”对您不利,那么您可能希望避免使用 Location 标头。

    一些提供 MP3 文件的 PHP 代码可以是:

    $file = 'local/path/to/your_audio_file.mp3'; // the path on the local file system - ie. don't use "http://www.example.com"!
    
    header('Content-Description: File Transfer');
    header('Content-Type: audio/mpeg'); // audio/mpeg is the correct mime type for mp3 files
    header('Content-Disposition: attachment; filename='.basename($file));
    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));
    readfile($file);
    exit; // Could be a good idea to exit at the end
    

    有些标头可能是多余的,但它们是为了确保最佳的跨浏览器兼容性。我从http://php.net/manual/en/function.readfile.php

    拿走了它们(然后改编)

    【讨论】:

    • 非常感谢 - 这非常有帮助。我尝试了您的代码(只是将 Content-Type 修改为 'application/zip',因为我的 MP3 已压缩),但得到了一个文件大小为零的 zip 文件。更多的谷歌搜索建议添加 ob_end_flush() ,这似乎已经成功了(即使我真的不明白为什么!)。如果我可以问一个后续问题:是否可以在下载发生时呈现页面(“您的下载将很快开始”消息),而不是有一个空白页面?我做了一些实验,但似乎无法将内容放在此旁边的任何地方......
    • 这只是一个重定向。您首先显示“下载开始”页面,然后重定向( 重定向)到 php.ini 中的下载页面。 Ps:不需要压缩mp3 :)
    • 啊好吧 - 这比我想象的要容易!感谢你的帮助。我正在压缩,因为它只是整张专辑(我就是这样的老派!)
    【解决方案2】:

    没有什么严重的问题,只是你实际上揭示了文件的实际位置(攻击者可能会尝试猜测他不应该访问的文件的地址),客户端将不得不向服务器发出额外的请求(延迟下载只是几毫秒) 有时,webbrowser 遇到了糟糕的一天,并出于各种原因决定不关注搬迁。

    从好的方面来说,这样你不需要通过 PHP 处理文件,节省了一点服务器资源(这是非常值得怀疑的,因为 readfile() 函数非常优化,与处理新的 http 请求相比)


    回到你原来的问题,当通过 PHP 提供文件以供下载时,你不需要以下示例中超过 3 行的内容:

    <?php
    //code copied from http://php.net/manual/en/function.header.php
    // We'll be outputting a PDF
    header('Content-type: application/pdf');
    // It will be called downloaded.pdf
    header('Content-Disposition: attachment; filename="downloaded.pdf"');
    // The PDF source is in files/originalfile.pdf
    readfile('files/originalfile.pdf');
    ?>
    

    请确保在使用 Header() 函数之前不打印任何字符串(甚至是源代码中的 UTF8 BOM 字符)

    【讨论】:

    • 谢谢大卫!正如我在其他评论中提到的那样,一旦我添加了 ob_end_flush(),这几乎就成功了。奇怪的是,使用我原来的方法,我从来没有看到显示的实际文件位置。我在 Chrome、Safari、FF、IE 中进行了测试,所有这些中的地址栏都没有改变。所以不确定用户将如何看到该位置?无论如何,这是学术性的,因为我现在可能会使用 readfile() 方法!
    • 我相信你要么犯了一些愚蠢的错误,要么你的脚本确实打印了一些不需要的东西。要分析第二种可能性,您可以使用这样的工具:rexswain.com/httpview.html;检查,没有打印不需要的字符,因为在使用 header() 函数之前不能打印任何字符
    • 谢谢大卫 - 下次我在电脑上时会检查一下。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-09-27
    • 2011-04-26
    • 2011-02-28
    • 2020-08-07
    • 2014-10-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多