WWW FAQs: How do I accept file uploads on a web page?

2006-09-01: File uploads are an often-overlooked feature of web browsers. Though most web developers know how to handle form submissions, not many know how to correctly receive file uploads. Fortunately, PHP makes this straightforward.

In this article, I'll show you how to accept file uploads with PHP. PHP is the most popular and easiest choice for extending websites to do "dynamic" things like handling file uploads. Of course, it can also be done with other languages. For information about handling file uploads in Perl CGI programs, see the CGI.pm documentation. For ASP.NET, see The Code Project's article, File Upload with ASP.NET.

Creating a File Upload Form

Those who have already designed web forms will know about the input element. You can create a file upload field with the following HTML:

<input type="file" name="myfile">

Simple, right? But it's not enough to do the job. There are two more requirements before a file upload button can work properly.

This article assumes you have dealt with form submissions before. If not, first read the article How can I receive form submissions?
Why must we use POST? Try to imagine the entire contents of a file appearing in the browser's location bar!
First, the form submission must use the POST method. GET is not permitted. So the method attribute of the form element must be set to POST.

Second, the traditional way of transmitting form data to the server, also known as the "enctype" application/x-www-form-urlencoded, is just too inefficient for file uploads. Uploaded files such as JPEG images can contain lots of special characters, and "escaping" those characters can easily triple the size of the file. That would mean very slow uploads.

So the major web browsers will simply refuse to upload files unless we set the enctype attribute of the form element to multipart/form-data. This encoding type sends the raw file data without escape characters, so it doesn't increase the time required to send the file.

Here's what the correct HTML for a file upload form looks like:

<form method="POST" enctype="multipart/form-data" action="upload.php">
<input type="file" name="myfile">
<input type="submit" name="submit" value="Upload Now">

Accepting the Uploaded File

So far, so good. But when that submit button is pressed, how do we get access to the file on the server side?

If this were an ordinary text field in a form, we could simply look at $_POST['myfile'] to see the text sent for the input element with the name attribute myfile. But that won't work for a file upload field. Because uploaded files can be very large, the contents of the file aren't "stuffed" directly into the $_POST associative array. Instead, the uploaded file is written to a temporary file on the server, and information about that file can be found in a separate $_FILES array.

The $_FILES array can tell us several useful things about an uploaded file. In fact, when we fetch $_FILES['myfile'], we get back another associative array with information about the file!

This sounds confusing but it's not. A simple example will help! Here's how we get the name of the temporary file that contains the contents of the uploaded file:


And here's how we fetch the original filename from the user's computer:


So how can we do something useful with the uploaded file? Well, it depends on what you want to do! If you're writing a tool that helps users upload web pages, then you'll just want to move the uploaded file to the correct place. And PHP provides a convenient function to do that in a single step.

But first, we need to pick a reasonable name for the final file on the server side. And we absolutely cannot trust $_FILES['myfile']['name'] to be reasonable or safe. That's because malicious hackers can and will upload files with carefully crafted filenames containing sequences of special characters like ../ that can be used to access directories that you never intended them to write to. Or worse. And, depending on your needs, you may also want to avoid overwriting an existing file with the same name.

Still, the name the user gave us is a reasonable suggestion, one we don't want to completely ignore. If the user uploaded a file called expensereport2006.html, then we want to use that name as our starting point.

The following code uses PHP's powerful "Perl-compatible regular expressions" feature to clean up the filename suggested by the user, and to avoid overwriting an existing file. The variable $finalDir should be set to the directory where you want the uploaded file to reside when the upload is complete.

For more information, see the PHP documentation for the preg_match() function.

$name = $_FILES['myfile']['name'];

# Go to all lower case for consistency
$name = strtolower($name);

preg_match('/^(.*?)(\.\w+)?$/', $name, $matches);

# $matches[0] gets the entire string.
# $matches[1] gets the first sub-expression in (),
# $matches[2] the second, etc.

$stem = $matches[1];
$extension = isset($matches[2]) ? $matches[2] : '';

# Convert whitespace of any kind to single underscores
$stem = preg_replace('/\s+/', '_', $stem);   

# Remove any remaining characters other than A-Z, a-z, 0-9 and _
$stem = preg_replace('/[^\w]/', '', $stem);

# Make sure the file extension has no odd characters
if (($extension != '') &&
  (!preg_match('/^\.\w+$/', $extension)))
  die("Bad file extension");

# Search for a unique filename by adding a number to the
# stem (first we try without, of course)
$suffix = '';
while (file_exists("$finalDir/$stem$suffix$extension")) {
  if ($suffix == '') {
    $suffix = '0';
  } else {

# Put the full name back together
$name = "$stem$suffix$extension";

Are you confused by the code "$name$suffix$extension"? Looks like a typo, doesn't it! But when you enclose text in " marks, PHP automatically inserts, or "interpolates," the contents of any variable names preceded by a $. So "$name$suffix" is just another way of writing $name . $suffix. Code written this way is often easier to read. And it's definitely less trouble to write. For example:


Is equivalent to:

"images/" . $name . $suffix . $extension . ".jpg"

Now you can copy the temporary file to a final location of your choice using the move_uploaded_file function of PHP:

move_uploaded_file($_FILES['myfile']['tmp_name'], "$finalDir$name");

The steps I've taken here to clean up the filename are acceptable for a general-purpose file upload utility. But if you plan to allow users to upload files for use by other users, you'll need to do more. As it stands, this code allows a user to upload any sort of file - an executable (.exe and several other extensions), a Word document (.doc), or an image (.jpg, .png, and .gif, among others), just to give a few examples. And some types of files can be actively dangerous. Executables are the most obvious threat - an executable program can do anything at all, including deleting every file on the end user's computer. Word and Excel files can also be dangerous, because they may contain "macros" that perform damaging actions - although Microsoft's latest versions of those programs do a better job of explaining the risks before running macro code downloaded form the Internet.

To be clear, this isn't a danger to your server, which merely stores the files. But it is a serious danger to users you then allow to download the files.

If your users understand that risk, there's no problem. But if you're running a public Internet site, and users are only supposed to upload images, then other users have a reasonable expectation that the uploaded files will be safe - they are "just pictures." And if we don't take steps to ensure that they really are just pictures, those users will be very angry.

How To Make Sure Uploaded Files Are Safe

If you don't want to deal with potentially dangerous files, the simplest general solution is to establish a list of file extensions you will accept, and reject any file that doesn't have one of those file extensions. Although the contents of the file might be dangerous executable Windows code, a Windows computer will never run that code if it has a .html extension. So if your upload page only accepts, for instance, .html, .jpg, .png, and .gif files, then there's no way to upload dangerous content.
Actually, image files can be dangerous - but only when new security holes in the web browser have been recently discovered. In the past, bugs have been found in major web browsers that could be exploited by uploading a very carefully crafted image file. A user accessing that file with an affected web browser version could lose control of their computer. Make sure you encourage your users to keep up to date with Windows Update, Firefox Update or the equivalent for their system. For the best safety, validate the image file yourself by opening and possibly re-saving it with an image manipulation tool like the gd library. This does carry a cost in performance and, for JPEGs, lost image quality because the image is compressed again.
Here's the additional code to accept only a short list of approved file extensions. Just do this before the search for a unique filename:

$safeExtensions = array(

if (!in_array($extension, $safeExtensions)) {
  die("File extension not approved");

And now you know how to deal with file uploads. Time to buy a bigger hard drive!

Legal Note: yes, you may use sample HTML, Javascript, PHP and other code presented above in your own projects. You may not reproduce large portions of the text of the article without our express permission.

Got a LiveJournal account? Keep up with the latest articles in this FAQ by adding our syndicated feed to your friends list!