【问题标题】:Force download with PHP then redirect使用 PHP 强制下载然后重定向
【发布时间】:2011-07-31 07:11:26
【问题描述】:

我知道这个问题以前被问过很多次,但我找不到适合我需要的答案。

我需要找到一种方法来强制下载文件,然后在下载开始后重定向到“感谢下载”页面。

到目前为止我有:

<?php
ob_start();

$token = $_POST['validationCode'];

if(isset($token)){

    $connect = mysql_connect('localhost', 'root', 'root');
    $db = mysql_select_db('mydb');

    if (!$connect || !$db){
        die('Connect Error (' . mysql_connect_errno() . ') '
                . mysql_connect_error());
    }

    $sql = mysql_query("SELECT * FROM emailaddresses WHERE token='$token'");
    $result = mysql_fetch_array($sql);
    if($result){
        header('Location: complete.php');
        header('Content-type: application/mp3');
        header('Content-Disposition: attachment; filename=track.mp3');
        $f = file_get_contents('downloads/track.mp3');
        print $f;
        $sql = "UPDATE emailaddresses SET download=1 WHERE token='$token'";
        $result = mysql_query($sql);
    }
    else{
        echo "There was a problem downloading the file" . mysql_error();
    }
}

ob_end_flush();

?>

隐藏下载文件的位置很重要,否则我会创建一个指向该文件的 HTML 链接。

我显然不能将重定向标头放在其他标头下方,因为它不起作用。除了在弹出窗口中打开它并将主窗口指向“谢谢”页面之外,我真的看不出从这里去哪里——但这是最后的手段。

谁能给点建议?

干杯,

丰富

【问题讨论】:

    标签: php redirect header download hide


    【解决方案1】:
    1. 您不能隐藏文件位置。任何有决心找到它的人都可以清楚地看到它,因为浏览器需要知道下载文件的 URL。
    2. 如您所说,您不能连续使用两个标头重定向来做到这一点。您只能在使用 Javascript 超时后重定向到其他页面。

    真的没有太多选择。如果您的主要目标是隐藏 URL,那么无论如何这都是一个失败的原因。为了获得良好的可用性,您通常在页面上包含纯链接(“下载未开始?点击这里...”),因为用户可能会意外取消在错误的时间重定向以不可撤销地终止下载。


    您不能在同一请求/响应中输出文件本身以外的任何内容。您可以按照@netcoder 的建议尝试多部分 HTTP 响应,但我不确定它的支持程度。只需假设存在一个“浪费的”请求/响应,其中仅下载文件而没有其他任何事情发生。通常使用此限制的方式是这样的:

    • 用户单击“下载”链接或使用其电子邮件地址或启动下载过程所需的任何内容提交表单。
    • 服务器返回“感谢您从我们这里下载!您的下载将很快开始...”页面。
    • 此页面包含 Javascript 或 &lt;meta&gt; 刷新或 HTTP Refresh 标头,导致延迟重定向到文件的 URL。
    • “谢谢”页面将“重定向”到文件位置,但由于这会导致文件下载,页面不会明显改变,只会启动下载。李>

    查看 http://download.com 以了解此操作的示例。

    您可以将文件的下载位置设置为仅在允许用户下载文件时才返回文件的脚本。您可以在“谢谢”页面和文件下载页面之间传递一些临时令牌,以验证是否允许下载。

    【讨论】:

    • 我知道该文件本身无法隐藏,但这纯粹是为了阻止该文件非常容易,因为有人需要做的就是输入他们的电子邮件地址来下载该文件。是否有办法将文件放在文档根目录之外,下载并重定向。您告诉我我不能这样做,但鉴于这种情况,您能否建议您如何最好地隐藏文件位置的一般大纲,一旦输入并验证了电子邮件验证代码,就强制它下载,然后重定向到另一个下载开始后的页面?
    • @richie 然后您需要对文件下载本身进行实际验证。正如您所说,将其放在 webroot 之外,通过脚本提供对它的访问,并让该脚本检查用户是否应该访问它。下载后重定向仍然存在,您必须使用 Javascript 客户端来完成。
    • 我的问题是在页眉将内容类型描述为应用程序的页面上回显某些内容(在本例中为 javascript),因此无论是否有输出缓冲,您都无法在标头之前或之后回显.我在这方面完全是菜鸟,所以我可能在我的假设中犯了一个根本性的错误,但我无法解决。不过到目前为止,感谢您的帮助-我相信明天我会在寒冷的光线下用这些答案解决它:)
    • 这听起来像门票谢谢!我已经有过几次了——在搞乱标题的同时——文件下载的地方,但它停留在验证输入页面上,而不是明显地移动到任何下载页面。我从没想过在“谢谢”页面上使用这一步骤。我敢肯定这需要一些摆弄,但我觉得我会把它整理好。谢谢!
    【解决方案2】:

    我所知道的 PHP 中唯一的方法(下载,然后重定向)是使用多部分 HTTP 响应。类似的东西:

    define('MP_BOUNDARY', '--'.sha1(microtime(true)));
    
    header('Content-Type: multipart/x-mixed-replace; boundary="'.MP_BOUNDARY.'"');
    flush();
    
    echo "Content-Type: application/zip\r\n";
    echo "Content-Disposition: attachment; filename=foo.zip\r\n";
    echo "\r\n";
    readfile('foo.zip');
    echo MP_BOUNDARY;
    flush();
    
    echo "Content-Type: text/html\r\n";
    echo "\r\n";
    echo '<html><script type="text/javascript">location.href="http://www.google.ca";</script></html>';
    echo MP_BOUNDARY.'--';
    flush();
    

    在您的情况下,您可以只输出“感谢下载”页面内容,而不是 JavaScript 重定向。

    我不确定它是否适用于所有/主要浏览器。

    【讨论】:

    • 这看起来很有希望,但我已经编写 PHP 两个月了,而且我对多部分 HTTP 响应一点也不熟悉!如果我无法得到与我当前知识领域相似的答案,那么我想这将是我需要学习的另一个领域!谢谢,里奇。
    • @richieahb:为什么不学习呢?这并不难。如果您知道如何处理标题和内容,那么您就知道如何处理多部分响应。我只是在那里做了一个例子。试试吧。 :)
    【解决方案3】:

    好的,首先,浏览器需要知道文件位置才能下载它。任何人打开像 Firebug 这样的标准浏览器开发工具都可以以纯文本形式查看文件的 URL。

    现在,我想您想保护您的文件免遭未经授权的下载。如果这是你想要的,有一种方法可以使用会话。

    在您的第一页上,您将输入您的代码来检查下载是否被授权。然后,您将使用可识别文件的内容将当前会话置于会话中。文件唯一的任何东西都可以,例如数据库 ID。

    $_SESSION['download_key'] = time();
    

    然后,您重定向到具有这样的 html 元数据的页面

    <meta http-equiv="refresh" content="5;/download.php?file=download_key" />
    

    这是你会说“谢谢你下载我很棒的文件的小伙子”的页面。请注意,如果您愿意,也可以将“content”属性的内容放在头文件中,像这样

    header('Refresh: 5;/download.php?file=download_key');
    

    请注意,5 是直到出现下载文件对话框的秒数。

    然后在 download.php 上您将执行以下操作:

    1- 检查使用 $_GET['file'] 请求的文件。

    2- 然后检查会话中是否存在 download_key。如果没有,你就这样退出脚本

    if (!isset($_SESSION['download_key'])) die('Unauthorized');
    

    3- 然后检查时间戳是否早于某个任意时间限制。这里有 30 秒

    if ($_SESSION['download_key'] - time() > 30) die('Unauthorized');
    

    4- 最后,如果全部签出,您就可以这样发送文件

    header('Content-disposition: attachment; filename=myfile.ext');
    header('Content-type: bin/x-file-type'); //Change for the correct mimetype
    readfile('myfile.ext');
    

    在readfile之后,将设置下载为1的代码放入数据库中。

    就是这样,受保护的文件下载,任何直接使用该 URL 的人都会看到一个很大的“未经授权”文本。

    我还想补充一点,如果你有一个大文件(超过几千字节),你最好禁用输出缓冲,因为这意味着 php 会在内存中保留文件的副本在整个下载期间。使用 readfile 函数,php 会在读取磁盘时将其发送到浏览器,因此会使用更少的内存(并且会更快地开始发送数据)。

    编辑:使它起作用的原因如下

    我实际上颠倒了顺序:访问者首先被重定向到包含刷新标题/标签的感谢页面。 Refresh 标头的神奇之处在于它在内容加载后重定向。一旦在感谢页面上,浏览器看到该标题然后在显示页面时等待指定的时间,然后重定向到下载。重定向后,浏览器会看到要下载的文件,而不是更改页面,只显示下载文件对话框。用户单击“确定”后,将开始下载,但他将停留在同一页面上。

    简而言之,您不需要在文件下载后重定向,因为您已经在感谢页面上!我认为文件下载后甚至不可能重定向。看看当您单击指向网络服务器上的直接文件的链接时会发生什么。浏览器提示下载,但不会切断您的导航。开始下载后,您可以愉快地点击离开带有链接的页面。下载结束时,您可能在一个完全不同的网站上。这就是为什么您之前只能显示感谢页面的原因。但是,如果您将刷新标题/标签设为零,则页面加载后会立即出现下载提示,因此几乎就好像两者是同时的(在访问者的眼中)

    【讨论】:

    • 谢谢你,那里有很多东西可以帮助我完善我所拥有的东西。但是,我不明白的是,当您访问此下载页面时会发生什么。因为您不能在标题之前回显任何内容,并且您不能在标题之后回显任何内容,因为它是作为文件的一部分读取的,所以如何在不使用背面的情况下将用户重定向/链接回任何内容的页面箭头!
    • 我试图在答案中添加更多细节。请告诉我是否还有一些不清楚的地方。
    • 感谢 Laurent - 这正是我现在所采用的方式。反过来做,只是让脚本改变标题而不是实际页面。感谢您提供有关使用大文件进行 ob-flush 的提示 - 没想到!
    猜你喜欢
    • 1970-01-01
    • 2011-09-24
    • 2013-04-02
    • 2010-10-23
    • 1970-01-01
    • 2011-06-10
    • 2013-08-08
    相关资源
    最近更新 更多