Recent Posts
- Quick Code: Get the domain name in JS
- Things to think about when designing a logo
- Javascript Array Functions
- Integrating FCKEditor to save current content with AJAX using PHP
- Pad A Javascript Number
- Why use heading tags as opposed to font tags for displaying text in HTML?
- Creating One Time Download Links
- Why Javascript in Forms is Bad
- Five Things To Remember When Designing Accessible Web Pages
- Sizing Images Correctly – Part Two
Topics
Creating One Time Download Links
Creating single use or timed download links can allow websites to offer downloadable content that expires after a single download or after a set period of time. Many e-commerce sites are using these techniques to sell documents and files online.
Graham, your other author here at Webvamp, has recently been working on a project where the client wanted to offer downloadable content to users that they could track and control access to. Due to the linking nature of the web this can be a difficult task if you just directly link to the file.
The user clicks on a link and views/downloads the file depending on if it can be displayed in the browser or not. Since the user is given direct access to the file, nothing is stopping them from sharing the link with other people and passing your content around to others. This is OK for free stuff, but not so great for content you are trying to sell. Directly linking to the file also makes it harder to keep statistics about how many times the file has been downloaded without crawling through your server logs.
In order to control access and gather information on the downloads he asked me how best to create a solution.
One Time Download Links
The solution is simple: offer unique download links.
Instead of directly linking to the file you want to download, you link to a download script that controls and manages the download process. You then have full control over any downloads you offer, allowing you to:
- Limit the number of times a file can be downloaded.
- Control when and for how long a download is available.
- Gather statistics on the number of downloads.
OK enough background, let’s see how to do it.
Show me the code!
The following code uses PHP and MySQL to offer a one time download link that lasts for 7 days. You can easily tweak it to meet your own needs and specifications.
SQL
CREATE TABLE downloads (
downloadkey varchar(32) NOT NULL unique,
file varchar(255) NOT NULL default '',
downloads int UNSIGNED NOT NULL default '0',
expires int UNSIGNED NOT NULL default '0'
);
PHP Code
//The directory where the download files are kept - keep outside of the web document root
$strDownloadFolder = "/downloads/";
//If you can download a file more than once
$boolAllowMultipleDownload = 0;
//connect to the DB
$resDB = mysql_connect("localhost", "username", "thisismypassword");
mysql_select_db("database", $resDB);
if(!empty($_GET['key'])){
//check the DB for the key
$resCheck = mysql_query("SELECT * FROM downloads WHERE downloadkey = '".mysql_real_escape_string($_GET['key'])."' LIMIT 1");
$arrCheck = mysql_fetch_assoc($resCheck);
if(!empty($arrCheck['file'])){
//check that the download time hasnt expired
if($arrCheck['expires']>=time()){
if(!$arrCheck['downloads'] OR $boolAllowMultipleDownload){
//everything is hunky dory - check the file exists and then let the user download it
$strDownload = $strDownloadFolder.$arrCheck['file'];
if(file_exists($strDownload)){
//get the file content
$strFile = file_get_contents($strDownload);
//set the headers to force a download
header("Content-type: application/force-download");
header("Content-Disposition: attachment; filename=\"".str_replace(" ", "_", $arrCheck['file'])."\"");
//echo the file to the user
echo $strFile;
//update the DB to say this file has been downloaded
mysql_query("UPDATE downloads SET downloads = downloads + 1 WHERE downloadkey = '".mysql_real_escape_string($_GET['key'])."' LIMIT 1");
exit;
}else{
echo "We couldn't find the file to download.";
}
}else{
//this file has already been downloaded and multiple downloads are not allowed
echo "This file has already been downloaded.";
}
}else{
//this download has passed its expiry date
echo "This download has expired.";
}
}else{
//the download key given didnt match anything in the DB
echo "No file was found to download.";
}
}else{
//No download key wa provided to this script
echo "No download key was provided. Please return to the previous page and try again.";
}
The above code is very simple, consisting of several checks to make sure that the download key provided in the URL is valid and then the download code.
All you then need to do is add entries to the database that link to the downloadable files. You could do this as part of your checkout process if you are selling the downloads, or as part of a registration or form request etc. Below is a basic example.
function createKey(){
//create a random key
$strKey = md5(microtime());
//check to make sure this key isnt already in use
$resCheck = mysql_query("SELECT count(*) FROM downloads WHERE downloadkey = '{$strKey}' LIMIT 1");
$arrCheck = mysql_fetch_assoc($resCheck);
if($arrCheck['count(*)']){
//key already in use
return createKey();
}else{
//key is OK
return $strKey;
}
}
//get a unique download key
$strKey = createKey();
//insert the download record into the database
mysql_query("INSERT INTO downloads (downloadkey, file, expires) VALUES ('{$strKey}', 'onetimedownload.zip', '".(time()+(60*60*24*7))."')");
All you then need to do is provide a link to the download.
Seeing it in Action
You can see a working example of this by downloading a zip file of this code.
The above link will take you to another page that will provide you with a unique download link to a bundled copy of the code. This unique download link will allow you to download the content once within the next 7 days.
Further Reading
Have Your Say
Comments
-
Jacob on 1st April 2009 wrote:
Hi Kirsty,
This error is shown when it cant find the file. I would suggest echoing $strDownload before the file_exists() function and then checking to make sure that the path it gives is 100% correct. Watch out for case-sensitive names.
If you still get errors try it with a file in the same directory as it could be a security setting that is preventing you from accessing a file outside of the directory.
Jacob.
-
joseph King on 16th April 2009 wrote:
I have tried numerous ways to get this to work but its not getting the file it gives me the error can not find file
We couldn’t find the file to download./onetimedownload.zip
i even placed the whole string including domain name sub folders and then the / even when the full path http://www.somethingcom/downloads/ was used and the echo replyedWe couldn’t find the file to download.http://www.somethingcom/downloads/onetimedownload.zip it still said we could not find the file. please help
-
joseph King on 16th April 2009 wrote:
I found the solution to the problem, where the download directory is “/downloads”/ you need to place a (.) before the first /. …example “./downloads/” this solved my problem with this script
-
Manny on 11th May 2009 wrote:
This works awesome but how can I get it to show the whole download path like it shows in the demo page?
currently is showing like this..
download.php?key=46fa4e30900a9fd100f53f8562eb9e1eI want it to show like
http://www.mysite.com/download.php?key=46fa4e30900a9fd100f53f8562eb9e1eany ideas?
-
Jacob on 12th May 2009 wrote:
Hi Manny,
I’ve just added the domain name to the start of the link in the demo.
So you know what your domain is and where your download.php script is held – so just add that to the front of the URL.
-
Manny on 12th May 2009 wrote:
Thanks that worked
On another note:What would stop the user from going back to the download area and having the index.php just re-generate a new key for a new download?
Any thoughts on preventing this :/
Thanks
-
Jacob on 12th May 2009 wrote:
Hi Manny,
Yes they could do on this example. But in a real world usage you would only ever create a key upon taking payment or subscription etc.
If your not taking payment or anything then you just need a section of code that tracks the user – usually this is done through a cookie or session etc.
-
Manny on 12th May 2009 wrote:
Thanks, Jacob I’ll see what I can come up with, just a matter of doing some research I guess.
-
wako on 9th December 2009 wrote:
Hi. I am stupid- please hold my hand.
I’d like to use this to offer a one-time download of a file (mp3) using about 500 password keys given out as physical coupons — so only the generated keys (handed out at a gig) will allow the download, and only one download per IP address.
I have looked at MySQL and read through this blog and I’m still bamboozled. Can someone please talk me through it like I’m five years old! In exchange I’ll give you unlimited downloads of the material!
Thanks
-
Son of 8-Bits on 30th January 2010 wrote:
If someone could make a Wordpress plug-in like this, I’d praise them into sainthood!
-
Martin on 9th February 2010 wrote:
Perfect nugget of code that’s given me the base for a simple download system for my customers.
Re: protecting the index page… Remember you can:
- rename it to whatever you want
- use htaccess rules to limit access to your static IP, an htaccess login or simply using noindex to hide your obfuscared alternative name for the file index.phpAs for generating multiple links (one per user)… You could simply build this script into a function and use it to generate the necessary code before popping it into your email program or whatever…
Hope that helps… and thanks Jacob for this nugget.
-
Chris on 11th February 2010 wrote:
I keep getting this error Warning: mysql_fetch_assoc() expects parameter 1 to be resource, boolean given in E:\www\webdirectoryname\download\index.php on line 20
Also, how do I go about adding links to the downloadable files in phpmyadmin?
-
Jacob on 11th February 2010 wrote:
@Chris – That means your SQL query hasnt returned a successful result to $resCheck. Add in:
echo mysql_error();
after the mysql_query() to see what the issue is.
You would have to cutomise phpMyAdmin to get direct links to the files. I just copy the keys to the end of the URL in the browse to get to them.
-
Chris on 11th February 2010 wrote:
@Jacob
Well I figured out the problem in my mysql error that I had. Everything works fine except my link is not generating any keys lol. I’m still a bit confused on how to link my files for download.
-
Jacob on 11th February 2010 wrote:
@Chris You first generate the key and store it in the DB with the downloadable file it relates to. Then you can use that key to create a link to the user.
If the download link is for a members account then you may want to store the download key in a membership table so you can always serve it to the user.
If your generating one time downloads for emails. Generate the key when creating the body of the email and include it as a link.
All the script is doing is hiding the real location of a file by saying ‘randomtext’ => ‘myfile.txt’.
-
Chris on 11th February 2010 wrote:
@Jacob
Okay. So I’m able to create a key in a link and then add it to the database but that’s not what I want to do.
On my website it says download.php?key= and-no-key-here. Usually if you keep refreshing the index page, it will generate a new key correct? But it’s not. If I click that link, it will call “No file was found to download.” It will generate a random key in the database but not in the
Kirsty on 31st March 2009 wrote:
Thank you for this script – it’s exactly what I’m looking for, however, I keep getting the first error in the download file ‘We couldn’t find the file to download.’ I’ve tried several locations using both absolute and relative addresses and getting the same error – what could I be doing wrong? Thanks for your help!