Recent Posts
- 10 Things To Do When Launching A Site
- Using PHP to connect to SFTP
- Asynchronous Virtual Pageviews with Google Analytics
- 5 Advanced Text Editing Keyboard Shortcuts
- Get the headers of a HTTP request with PHP
- How-to: Create PDF preview images in PHP – Part 2
- How-to: Create PDF preview images in PHP
- Quick Code: Get the domain name in JS
- Things to think about when designing a logo
- Javascript Array Functions
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
-
Chris on 11th February 2010 wrote:
(continued) Apparently you cant write html in this form.
….but not in the href=”download.php?key=key-should-be-here”
-
Sanna on 16th June 2010 wrote:
Some people are having issues with the download key. It can be due to escape characters. I realised I got the same issue when using php echo in the front of the download link in my file. Here is how I escaped it and also removed from the link:
echo “
download.php?key=$strKey
This link will allow you to download the source code a single time within the next 60 minutes.”;I hope this helps somebody else again.
Thanks for the code fro Webvamp!
-
Sanna on 16th June 2010 wrote:
oh, also this didn’t let me to put it the way I wrote here… may need some escaping as well here, so just try to learn something about escaping in order to fix the key issue…
and remove the on the both sides of the $strKey
-
manny on 5th August 2010 wrote:
Dumb question, how do I adjust the time on this so the link with expire in 5 hours instead of 7 days.
Thanks in advance!
-
manny on 5th August 2010 wrote:
I thought I had this working before, but I had to reinstall it. now I’m back to getting the error message
We couldn’t find the file to download.
I created a folder outside the public_html folder and called it downloads
I copied my file into it and made the name change to themysql_query(“INSERT INTO downloads (downloadkey, file, expires) VALUES (‘{$strKey}’, ‘myfilename.zip’, ‘”.(time()+(60*60*24*7)).”‘)”);
still getting the error finding file
-
Jacob on 12th August 2010 wrote:
@manny – To change the time limit you change the following:
“..time()+(60*60*24*7)…”
This value is the number of seconds from when you create the download. So 5 hours would be:
(60*60*5)
60 seconds in a minute, 60 mins in an hour, multiplied by 5.
-
Jacob on 12th August 2010 wrote:
@manny – If you get that error it means the file cannot be found, which likely means you do not have the full path correct.
Echo $strDownload and see what it says, it should be the full path to the file on your server. Edit the $strDownloadFolder parameter at the top of the script to get the path correct.
Examples of correct paths could be:
$strDownloadFolder = “/home/webvamp/www/downloads/”;
$strDownloadFolder = “C:/sites/webvamp/downloads/”;
It all depends on your server and where you store the file.
-
Dominique Louis on 4th January 2011 wrote:
Please help, I am new to php and I am getting the following error. I am also not clear on hot to link the file from the database. can you please elaborate on that. Warning: mysql_fetch_assoc(): supplied argument is not a valid MySQL result resource in /home/content/d/e/n/dennis40/html/download/index.php on line 20
-
Jacob on 17th January 2011 wrote:
@Dominique – It means the SQL query was invalid and didnt return an results. Try adding in “echo mysql_error()” just above line 20 and see what it says – it will likely say that there is an error in your SQL syntax, and you just have to fix that.
-
Jasica on 17th February 2011 wrote:
YAHOOOO! JACOB YOU ROCK MY WORLD BUDDY, i been looking for a script like this and everything is working supper great. i would like to ask how can i make it only one time a user can download it’ as soon they finish downloading the link will expired..? and stop them from trying to download again
-
Jasica on 17th February 2011 wrote:
what i meant is how can i Limit the number of times a file can be downloaded. i only want the user to download only one time no more then one time ? please help thanks for the great script
-
Jacob Wyke on 17th February 2011 wrote:
@Jasica – To only allow a single download per line just ensure the following line of code:
$boolAllowMultipleDownload = 0;
This ensures that the links only work ONCE. If you want them to work multiple times change this to:
$boolAllowMultipleDownload = 1;
-
Jasica on 17th February 2011 wrote:
JACOB: Thanks’ I have check the code it was already setup $boolAllowMultipleDownload = 0;
They was no need for me to change but i did change the time to 60*60*1 , I am hoping that would be one hour after that it will expire and generate new key.. after hour i come back to see if i will be able to download the link i was still able to download and refresh, shouldn’t it say sorry you have already downloaded this file..?
-
Jasica on 17th February 2011 wrote:
Oh i get it know i see how it work, i was ruining the main index file
, i was wondering is they a way that a user can click the download link and it will let them download only one time. because right now it seem like i have to give out the download link every time to the new user. please get back to me at my email address. thank you soo much i love the script
-
Jacob Wyke on 24th July 2011 wrote:
@Demetrio It could be a PHP setting, either its timing out of memory usage is being hit.
-
ken on 9th October 2011 wrote:
ok…I am a complete idiot and a PHP newbie at that…..I download this code, saved it as “download.php”, added a link to it (downlaod.php) but when i click the link i get a message error that no key has been provided. If i look in the DB table a key is generated each time i click the link but the page being called is not getting shown ????? Can ANYONE help…?
-
ken on 9th October 2011 wrote:
for others who may run into the same problem i did….instead of waitinf or a response or spending hours searching for answers….simply change the code on the index.php to [php]$resCheck = mysql_query(“SELECT * FROM downloads WHERE downloadkey = ‘”.mysql_real_escape_string($_GET['key']).”‘ LIMIT 1″);[/php]
-
ken on 9th October 2011 wrote:
ok….here is a NEW POST / QUESTION ?????? How can you expire the link instead of making it available for days or hours. I need a link to expire once the file is downloaded…..PLEASE RESPOND ASAP
-
Jacob Wyke on 19th October 2011 wrote:
@ken – There is a variable in the code to do this:
//If you can download a file more than once
$boolAllowMultipleDownload = 0;This means you can only download it once.
-
Paola on 16th November 2011 wrote:
Thanks for the code, is excellent. I changed so that instead of downloading the file, will open a Web page, it works fine but I need to send the link via email. Mail arrives along with the link. The problem is that not open the link is in the database, but it opens the form of email instead. The data get inserted to the database. One more thing, link never expired. Why that happen?
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!