Speed Limiting File Downloads
By Peter McNulty : 15-12-2004
Creating a download script
It is common to have downloads on your website, and sometimes you want to moderate your downloads by speed, or possibly hide the directory that the download is coming from, protecting all your other files. This is very possible and very simple to solve in PHP. All you need is the header() function and some files function.
The header() function
The header() function is a predefined function in PHP which allows us to send raw HTTP headers to the browser. HTTP headers are what controls almost everything the browser will do when it receives information from your website. The general format of header() is:
void header ( string string [, bool replace [, int http_response_code]])
This indicates to us that the string called string is required and the other parameters are not. The string parameter is the actual raw header that you are going to send to the browser and this is the most important parameter. We won't deal with the other ones, however, replace will cause this new header to overwrite any similar headers, and http_response_code will force the response code to a certain value, i.e. 404 for error pages.
An important feature in the use of header() is that it must come before any text has been outputted to the screen. Hence, this is not valid:
This will definitely cause an error in your script if you tried to run it. Instead, we must do something like the following:
This example script, would output the 404 error code to indicate a page not found.
The download script
What we are now going to do, is create a PHP file, which first will output the correct HTTP headers to indicate to the browser that we are doing a download. Then we are going to open the file that you want to be available to download, and we are going to send it to the output.
Whilst we are sending it to the output, we are going to moderate how much gets sent each time, therefore limiting the download speed. This is particularly useful if you have a high bandwidth site, and you must limit the speed of downloads to give a fair usage.
So, first we define our file location, name and speed parameters:
Nothing too special about the above script, except that we are limiting our download speed to 10 kB/s. That is 10 * 1024 bytes or 10 * 1024 * 8 bits. We then send our special headers:
The header Cache-control tells the browser not to look for the file in cache that we are sending to be downloaded. Content-Description is just a text description of what we are doing. We could set this value to anything we wanted. Content-Type is set to application/force-download, this tells the browser that we are sending a file, which must be downloaded, it cannot be displayed in the browser. This prevents images such as .gif and .jpg from being displayed in the browser, but being forced to download them. Finally, the last two headers just specify the size of the download and the name of the download.
Next up, we read the file, and send it to the output:
Read the file
That shouldn't be too complicated to understand. First the file is opened in read-only mode, and if that fails, we exit the script to try and prevent any download from beginning. We then run a while loop until the end of the file (eof) is found. Inside this while-loop, we read in a block of data using fread(). This takes the file handle we are accessing and the amount of data to read in bits. Hence, we must multiply our speed by 1024 * 8 to get it into bits.
Next comes the two important steps into speed limiting. First we use flush() which will send all the contents of the buffer to the webpage immediately. Usually, the whole page is parsed, and then the contents are sent, but using flush() we can send the contents as soon as they are ready. Next we use sleep(1) which completely pauses the execution of the script for 1 second each loop. This is what produces are download speed per second.
Finally, we just close the file and we're done. In a normal situation, we would gather the file name and location from a database so that this can become a generic file downloader, however, that is out of the scope of this article.
NOTE: If you find your script is timing out, add set_time_limit(0); to the top of your code to let the script run indefinitely.
Page 1 of 1