Uploading Files with PHP

Overview

This is simple tutorial that explains how to create an HTML form and use it to upload files with PHP. First, you will learn how to create the form. Then, you will learn how to receive the file via PHP and safely move it from the server's temporary directory into a more usable location.

Creating the Form

First, we're going to create an HTML form which will allow users to select a file. The form will consist of a file input and a submit button:

<form action="" method="post" enctype="multipart/form-data">
    Select a file to upload<br />
    <input type="file" name="user_file" />
    <input type="submit" value="Upload" />
    <input type="hidden" name="MAX_FILE_SIZE" value="2097152" />
</form>

The required pieces that make file uploads work are the enctype attribute and the MAX_FILE_SIZE hidden input element. The value of MAX_FILE_SIZE is in bytes, so 2097152 bytes is equal to 2MB. For 5MB you would put 5242880. (For an easy way to convert megabytes to bytes, check out the Google Calculator)

Note that the action attribute is empty. This will cause the form to POST to itself. It is acceptable, though not necessary, to set this value to another page.

Now, we have a fully functional form that will upload a file to the server.

Receiving the Uploaded File

When a file is uploaded in this manner, it is automatically renamed and stored in the server's temporary directory. The names and locations will vary on different systems and server configurations, but the values are passed in the PHP $_FILES array so you can easily access them.

Let's say our user uploads clowns.jpg to our server. When the form posts and the upload completes, the $_FILES array will look something like this:

Array
(
    [user_file] => Array
        (
            [name] => clowns.jpg
            [type] => image/jpeg
            [tmp_name] => /var/tmp/phpB3AA.tmp
            [error] => 0
            [size] => 10186
        )

)

The name element consists of the orignal filename, while the tmp_name element is the actual location on the server. The mime-type is stored in type, the file's size (in bytes) in size, and the upload error code in error (error code 0 means the upload was successful — refer to the PHP manual for a detailed explanation of PHP's file upload error codes).

At this point, PHP can read and write to the file from its temporary location, which is fine for some applications, but if you require that the file remain somewhere on the server or be accessible via the Web, you will need to move it to a safe location and rename it appropriately. To do this, it is best to use the move_uploaded_file() function.

Let's move and rename the file from /var/tmp/phpB3AA.tmp to /www/my_domain/files/clowns.jpg:

<?php

$from = $_FILES['user_file']['tmp_name'];
$to = '/www/my_domain/files/' . $_FILES['user_file']['name'];

if( move_uploaded_file($from, $to) ) {
    echo "Upload successful :)";
} else {
    echo "Upload failed :(";
}

?>

Note that, if the same file exists in the new location, it will automatically be overwritten!

That's it, you're done! Just make sure you read the security warning below to ensure that you know how to protect your upload script(s) from being exploited.

Security Warning

Extreme caution should be taken when allowing the public (or any untrusted source) to upload files to your server. You should always check file extensions of uploaded files and disallow any potentially harmful filetypes (i.e. PHP, ASP, CF, etc.).

The reason? Server-side scripts that can be uploaded into a Web-accessible location can sometimes be executed by the uploader. A malicious person could use this exploit to gain access to scripts, data, usernames/passwords, or other sensitive information. It may even be possible for them to compromise your entire server.

It is recommended that, instead of disallowing a bunch of file extensions, you should allow only certain extensions that you specify. For example, if you are creating an image uploader that can upload JPEG, GIF, and PNG files, you should verify that the file uploaded has a JPG, JPEG, GIF, or PNG extension. (My check_extension() function may be useful in cases like this)

This is especially true if you don't maintain your own Web server. Imagine that you block out all of the scripting extensions that your Web host supports, and a month later they decide to add Ruby on Rails to their list of features without informing you (or, perhaps, you simply don't read the email they sent). Your upload script, and therefore your entire Web server, may become vulnerable if an attacker successfully uploads a malicious Ruby script.

Author avatar

About the author

New Hampshirite building web apps in Florida. Creator of Surreal CMS, Postleaf, and DirtyMarkup.

Need to get in touch? Catch me on Twitter.