A Beginners Guide to PHP Download Scripts

| Coding Tutorials
A Beginners Guide to PHP Download Scripts

Note: At the time of writing this tutorial, the author didn't put much look into handling security threats that is associated with the file download. This code is not intended for real-world use, without further security hardening. Please read the comments below for more details. Also, download this file for additional sanitation and security code.


In this tutorial, I'm going to show you how to write a PHP script that allows downloads.

To allow downloads from a server, you need to write a script that can communicate with it effectively.

PHP is a server-side scripting language and is well-designed for this task, with many versatile tools. I'm going to show you how to power the download using the HTTP header function.

Let's take a look at the HTTP header function. This function is used to send a raw HTTP header to a client:

header( string, replace, http_response_code );

Let's examine the three parts of that function:

  • string: This is a required parameter and specifies the header string to send.
  • replace: This is an optional parameter and its indicate whether or not to replace the previous header or add a second header. The default is TRUE which will replace the header, whereas FALSE allows multiple headers of the same type.
  • http_response_code: This is an optional parameter and forces the HTTP response code to the chosen value.

Here's the function inside a complete download script:

 // check if the download button is clicked if ( isset($_POST['downloadButton'] )) { // check if the filename is set $filename = ( isset($_POST["filename"]) ? $_POST["filename"] : null ); // check if file is in the directory or on the server if ( file_exists( $filename )) { // download the file from the server header("Content-Type: application/octet-stream"); header("Content-Disposition: attachment; filename=filename here"); header("Content-Length: " . filesize( $filename) ); header("Cache-Control: must-revalidate"); readfile( $filename ); exit; } } 

You can click here to download a working copy of the code in this tutorial.

Let's break down the examples of the HTTP header function in that code:

Content-Type

This declares the file as a binary and setting its type. The HTTP Header string parameter is set to Content-Type: application/octet-stream, or to the specified file type if needed. This enables the browsers to treat the file as a binary. Note that application/octet-stream can be used to dynamically refer to all file types.

header("Content-Type: application/octet-stream");

Content-Disposition

The HTTP Header string parameter is set to Content-Disposition: attachment. This forces the browser to display a download dialog box, thereby making the download possible.

header("Content-Disposition: attachment");

By using the HTTP header Content-Disposition: attachement, you can also supply or add a recommended filename to be displayed by the download dialog box. This is done using a concatenated filename attribute.

header("Content-Disposition: attachment; filename=file name here");

When no filename is specified, the current script filename is used. Also, avoid separating the attribute filename with a blank space like so, file name=file name here, because blank spaces will break the script.

Content-Length

The HTTP Header string parameter is set to Content-Length: filesize=file size here. This is used to display the file size information in the download dialog box. To easily get the file size, you will use the PHP filesize( ) function and pass the filename to its parameter.

header("Content-Length: filesize=" . filesize("file name here") );

Here is the filesize() function which returns the size of the specified file.

filesize( string filename );

Cache-Control

The HTTP Header string parameter is set to Cache-Control: no-cache, or Cache-Control: must-revalidate. This is because most information about the file is cached, so it's important to control the cache.

header("Cache-Control: no-cache");

The readfile function

To retrieve the actual file contents form the server, you can use the PHP readfile() function. This file function comes in handy because you don't necessary need to write any conditional loop statements to loop over all of the file's data.

This function reads a file and writes it to the output buffer. It returns the numbers of bytes read on success, or FALSE and an error on failure.

readfile( filename, include_path, context );

  • filename: This is a required parameter. It specifies the file to read.
  • include_path: This is an optional parameter. Set this parameter to '1' if you want to search for the file in the include_path (in php.ini) as well.
  • context: This is an optional parameter. Specifies the context of the file handle. Context is a set of options that can modify the behavior of a stream.

Security checks and limitations

To prevent the user from downloading any files from the server by altering the script, the filename should not be passed as a query string in the URL using the $_GET method. Always use the $_POST method to send the filename along with the form whenever it is submitted.

You can use the HTTP header Content-Type : file type to limit the files users can download from the server. For example, if you have an image file with .png extension on the server, and you want users to be able to download it, instead of using Content-Type: application/octet-stream, you can set Content-Type header to the specified file type like so - Content-Type: image/png. This will limit the types of file users can download, and also, prevent them from downloading sensitive content from the server.

Why is this important? If you send a URL query like so localhost/download.php?filename=download.txt, the user can try to manipulate the URL query and change it from ?filename=download.txt to this - ?filename=anything. Or the user may try a URL query injection, which might break your script.


About the author

John Zenith is a front-end developer, writer, speaker, and a code builder who loves Javascript, CSS and PHP. He lives in the southern part of Nigeria.