Force download with PHP
30 March, 2009
Sometimes when you are linking to a file, you want the user to download that file rather than view it in their browser. This applies to file that they can view, such as images (JPEG, GIF, etc.), PDFs, MP3s, etc.
You need to force the user to download the file. We can do this with PHP quite easily. However, there are a few considerations to bear in mind, and we will address these shortly. First, let's see an example.
Below are four links to two files. The top link to each file opens it in the browser, in a new window. The second link to each file forces user to download the file.
So as you can see, one link shows the element in the browser as per normal, the second link forces the download option upon you. How is this done? Well, when we want to force the download we direct the user to a PHP page (in this case download.php) which then delivers the file to the user. Let's take a look at the code we need to put on download.php.
$filename = $_GET["file"];
$file_extension = strtolower(substr(strrchr($filename, "."), 1));
if ($filename == "") {
echo "<html><title>Download</title><body>No filename given.</body></html>";
exit();
} elseif (!file_exists($filename)) {
echo "<html><title>Download</title><body>File not found.</body></html>";
exit();
}
switch ($file_extension) {
case "pdf":
$contenttype = "application/pdf";
break;
case "jpg":
$contenttype = "image/jpg";
break;
}
header("Content-Type: " . $contenttype);
header("Content-Disposition: attachment; filename=\"" . basename($filename) . "\";");
readfile($filename);
exit();
Now, it is important to note that this is the minimum amount of code to get this procedure to reliablly work. We will look at extra code we can add shortly.
The first part of our code simply gets the filename we passed in through the URL, gets the file extenstion and then performs a couple of checks. We check that filename is present, and that the file does exist. If either of these checks fail, we display an error message on end the process.
If that is in order, we carry on. We set the content type based on the file extenstion, and then we deliver the file to the user before ending the process. It's quite simple when the code is cut down the minimum like this.
The URL we give the user is simple as well. Just download.php with file attached:
<a href="download.php?file=image.jpg">download.php?file=image.jpg</a>
And that's the basics of forcing the content to the users. Now we will look at some important issues with this method, and some more code to make things a little nicer.
The first major issue we run into with this procedure is security. Using this code will create a massive security flaw. The above code will download any file that is passed in through the URL. That means a villan could pass in a string such as "../../conn.php" and then download your raw database connection file. This will give them your database username and password. Any file can be downloaded this way. So what can we do?
There are more than a couple of options, but we will look at two easy ones. The first option is to only allow explicit files to be downloaded. A simple if statement checking the filename will suffice here. Something like:
if ($filename != "pdfdocument.pdf") {
exit();
}
This will cause the process to end if the filename passed in is not "pdfdocument.pdf". Of course, you might want to display and error message or something like that to inform the user.
Another method that could be used is to store the file names and/or locations in a database, then only pass through the ID number of the file to download. Based on this ID you could retrieve the correct path to the file. This way any file that is not in the database cannot be downloaded.
There are other methods of course which will work better and may be more complex. Applying a little bit of thought to the problem is bound to come up with a solution that works for you, but it is important to recognise this risk before implementing a system like this on your site.
Some other content types that can be used can be seen below. These can simply be added to the switch statement above.
case "exe":
$contenttype = "application/octet-stream";
break;
case "zip":
$contenttype = "application/zip";
break;
case "doc":
$contenttype = "application/msword";
break;
case "xls":
$contenttype = "application/vnd.ms-excel";
break;
case "ppt":
$contenttype = "application/vnd.ms-powerpoint";
break;
case "gif":
$contenttype = "image/gif";
break;
case "png":
$contenttype = "image/png";
break;
default:
$contenttype = "application/force-download";
There are also a few header parameters we can add in, such as:
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("CCache-Control: private", false);
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($filename));
And that concludes our article. The technology is there, but use it wisely. Please. This method can be quite handy, but it is not without risk.
The code we have used at fcOnTheWeb, which we have then based this article off, can be found at http://elouai.com/force-download.php. We created our PDF document with PDF Online.
ferrari_chris



Add your comment on this article below:
Sorry, there's an error with your form entries. We really appreciate your comment, so please try again.
Form submitting now...