开发者

Force download with PHP then redirect

开发者 https://www.devze.com 2023-02-22 09:51 出处:网络
I know this question has been asked many times before but I can\'t find an answer to suit my needs. I need to find a way to force the download of a file and then, after the download has started, redi

I know this question has been asked many times before but I can't find an answer to suit my needs.

I need to find a way to force the download of a file and then, after the download has started, redirect to a "thanks for downloading" page.

So far I have:

<?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();

?>

It's important to hide the download开发者_如何学Go file's location otherwise I would have just created an HTML link to the file.

I obviously can't put a redirect header below the other headers as it just won't work. I can't really see where to go from here apart from opening this in a pop-up and directing the main window to the "thank you" page - but that is a LAST resort.

Can anyone give any suggestions?

Cheers,

Rich


  1. You can't hide a file location. It'll be plainly visible to anybody determined enough to find it, by the very necessity that the browser needs to know the URL to download the file.
  2. You can't do it with two header redirects in succession, as you said. You can only redirect to a different page after some timeout using Javascript.

There really isn't much choice. If your primary goal is to hide the URL, that's a lost cause anyway. For good usability, you usually include the plain link on the page anyway ("Download doesn't start? Click here..."), since the user may accidentally cancel the redirect at just the wrong time to irrevocably kill the download.


You cannot output anything other than the file itself in the same request/response. You could try multi-part HTTP responses as suggested by @netcoder, but I'm not really sure how well that's supported. Just start with the assumption that there's one "wasted" request/response in which only the file is downloaded and nothing else happens. The way things usually work with this restriction is like this:

  • User clicks "download" link or submits form with his email address or whatever is required to initiate the download process.
  • Server returns the "Thank you for downloading from us! Your download will start shortly..." page.
  • This page contains Javascript or a <meta> refresh or HTTP Refresh header that causes a delayed redirect to the URL of the file.
  • The "Thank you" page will "redirect" to the file location, but since this causes the file to download, the page will not visibly change, only the download will be initiated.

Look at http://download.com for an example of this in action.

You can make the download location for the file be a script that only returns the file if the user is allowed to download the file. You can pass some temporary token between the "Thank you" page and the file download page to verify that the download is allowed.


The only way in PHP I know of (to have a download, then to redirect) is to use a multi-part HTTP response. Something like that:

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();

In your case, you could just output the "Thanks for downloading" page content instead of the JavaScript redirect.

I'm unsure whether it works on all/major browsers or not.


Ok, first thing first, the browser need to know the file location to download it. Anyone opening a standard browser dev tool like Firebug will be able to see in plain text the URL of your file.

Now, I suppose you want to protect your file from unauthorised download. If that is what you want, there is a way for that using session.

On your first page, you will put your code to check if the download is authorized. You will then put in session the current with something that identify the file. Anything that is unique for the file will do, like the database id.

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

Then, you redirect to a page with a html meta like this

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

This is the page where you will say "Thank you fine lad for downloading my awesome file". Note that you can also put the content of the "content" attribute in a header file if you like, like this

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

Note that the 5 is the number of second until the download file dialog box appear.

Then on download.php you will do the following :

1- Check which file was requested using the $_GET['file'].

2- Then you check if the download_key exists in session. If not, you exit the script like that

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

3- Then you check if the timestamps is older than some arbitrary time limit. Here with 30 sec

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

4- Finally, if all check out, you send the file like that

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

After the readfile, you will put the code to set the download to 1 in the database.

And that's it, protected file download, and anybody using the URL directly will be greeted by a big "unauthorized" text.

I'd also like to add that if you have a big file (more than say a few kilobyte), you may be better off disabling output buffering, since that will mean that php will keep a copy of the file in memory for the whole duration of the download. With the readfile function, php will send it to the browser as it read it on the disk and thus will use less memory (and will start to send data sooner).

EDIT : What make it work is the following

I actually inverted the sequence : the visitor is first redirected to the thank you page which contain a Refresh header/tag. The magic of the Refresh header is that it redirect AFTER the content is loaded. Once on the thank you page, the browser seeing that header then wait for the specified time while showing the page, then redirect to the download. Once redirected, the browser see that its a file to be downloaded and instead of changing the page, just show the download file dialog. Once the user click OK, the download will start but he will stay on the same page.

In a nutshell, you don't need to redirect after the file download, since you are already on the thank you page! I don't think it's even possible to redirect after a file download. Just look at what happen when you click on a link that point to a direct file on the webserver. The browser prompt for download, but does not cut your navigation. Once the download is started, you can happily click away from the page with the link. By the time the download is over, you may be on a completely different Website. That is why you can only show the thank you page before. But if you put a zero for the refresh header/tag, the download prompt will appear as soon as the page is loaded, so it's almost as if the two were simultaneous (in the eye of the visitor)

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号