http://www.developer.com/

Back to article

Uploading Files within a PHP Script


September 3, 2002

Being able to accept uploads from your users is one of those surprisingly essential functions that re-occur in many applications. Examples are everywhere: on bulletin boards people are often allowed to upload attachments and custom avatars, content management systems allow administrators to upload images for posts and web site management scripts utilize uploads to give users to ability to edit and add new files.

Adding file uploads to your own scripts is relatively easy. Because HTML includes a file upload form field: <input type="file" name="file"> the client side task of selecting a file is handled for you by the browser.

When including a file upload field in a form, an additional attribute also must be added to the form tag: enctype="multipart/form". This tells the browser that in addition to the standard text, a file may be send with the form. Also, the form method should always be POST rather than GET for the file to be sent.

Pay particular attention to the form tag and file input field as we take a look at a simple page containing a form:

<html>
<head></head>
<body>

<form action="<?=$PHP_SELF?>" method="post" enctype="multipart/form-data">
<br><br>
Choose a file to upload:<br>
<input type="file" name="file"><br>
<input type="submit" name="submit" value="submit">
</form>

</body>
</html>

If the user selects a file and submits the form, then the file will be uploaded and stored in a temporary directory on the server with a temporary name. This is when the real programming begins.

Accessing Uploaded Files

Just like other form elements, the value of the file field can be accessed by with the variable name that matches the name of the input; in this case "file". Unlike other form variables though, variables that reference uploaded files are arrays, and contain additional useful information:

$HTTP_POST_FILES['file']

the parent array, where 'file' is the name of the file input field in the form.
$HTTP_POST_FILES['file']['name'] the original name of the file from the user's computer.
$HTTP_POST_FILES['file']['tmp_name'] the temporary name assigned to the file when its uploaded.
$HTTP_POST_FILES['file']['type'] the mime type of the file, as provided by the user's browser
$HTTP_POST_FILES['file']['size'] the size of the file, in bytes.

Validation

Include a reminder on your form telling users that they should only upload gif files under 10kb, and you can bet that a good half of them will try to upload 10mb executable files. It is for this reason that validating uploaded files is particularly important.

Typically, there are three checks that should be performed on incoming files:

1.) that a file was actually uploaded. If one was...
2.) that it is under under a specified file size and...
3.) that it's file type is one of those that you want to accept.

A few if statements is all that is needed to perform this type of validation, and even create some friendly error messages to display to the user in case of one the checks fails. Let's take a look at how it can be done.

There are several ways to see whether a file has been uploaded, depending on which version of PHP you use. The most secure and accurate way is with is_uploaded_file(), available in versions of PHP3, and PHP4.0.2 and higher:

if (!is_uploaded_file($HTTP_POST_FILES['file']['tmp_name'])) {
  $error = "You did not upload a file!";
  unlink($HTTP_POST_FILES['file']['tmp_name']);
} else {
  //a file was uploaded
}

The if statement checks to see if a temporary file was created with the specified name and if so, that it is an uploaded file. If you want to require the user to upload a file, you can create a variable to hold an error message, and stop processing the form entirely at this point. If a file has been uploaded, then the contents of the else statement will execute. This is where all of the other error checking a processing for the file should be nested, so that the next block of error checking code only executes if upload has passed the first.

The next thing we need to validate is the size of the file. For the purposes of this example, let's say that you wanted to only allow files under 10kb. First, we'll assign the maximum allowed file size to a variable (in bytes), then check it against the actual size of the uploaded file (in bytes):

$maxfilesize=10240;

if ($HTTP_POST_FILES['file']['size'] > $maxfilesize) {
  $error = "file is too large";
  unlink($HTTP_POST_FILES['file']['tmp_name']);
} else {
  //the file is under the specified number of bytes.
}

Again, if the uploaded file fails the check, we will generate an error message and stop processing the form. In addition, because a file was successfully uploaded, we need to remove it from the server in case of an error using the unlink() function.

The final check performed on the file is to validate the file type. You might consider doing this by checking the extension of the filename the user uploaded. The problem with this method is that file names can be altered; the user could easily rename an .exe file to have a .jpg extension, and no one would be the wiser.

For a little more secure file type detection, we can make use of the $HTTP_POST_FILES['file']['type'] variable, which contains the (harder to alter) mime-type of the file. In this example, we will just check to make sure the file is a jpg or gif:

if($HTTP_POST_FILES['file']['type'] != "image/gif" AND $HTTP_POST_FILES['file']['type'] != "image/pjpeg" AND $HTTP_POST_FILES['file']['type'] !="image/jpeg") {
  $error = "This file type is not allowed";
  unlink($HTTP_POST_FILES['file']['tmp_name']);
} else {
   //the file is the correct format
}

Again, if our validation fails, we assign an error message and remove the uploaded file. If it is successful, this point, we can copy it to its final destination; giving it a new name, or using the original one from the user's computer:

copy($HTTP_POST_FILES['file']['tmp_name'],"/finallocation/".$HTTP_POST_FILES['file']['name']);

And after the file has been copied to its final location, we can remove the temporary file:

unlink($HTTP_POST_FILES['file']['tmp_name']);

To make sure that the code to handle the upload only executes when the form is actually submitted, the entire block is wrapped up within an if statement that check to see if the submit button has been pressed. Take a look at the entire script:

<?php

if ($HTTP_POST_VARS['submit']) {
  print_r($HTTP_POST_FILES);
  if (!is_uploaded_file($HTTP_POST_FILES['file']['tmp_name'])) {
    $error = "You did not upload a file!";
    unlink($HTTP_POST_FILES['file']['tmp_name']);
    // assign error message, remove uploaded file, redisplay form.
  } else {
    //a file was uploaded
    $maxfilesize=10240;

    if ($HTTP_POST_FILES['file']['size'] > $maxfilesize) {
      $error = "file is too large";
      unlink($HTTP_POST_FILES['file']['tmp_name']);
      // assign error message, remove uploaded file, redisplay form.
    } else {
      if ($HTTP_POST_FILES['file']['type'] != "image/gif" AND $HTTP_POST_FILES['file']['type'] != "image/pjpeg") {
        $error = "This file type is not allowed";
        unlink($HTTP_POST_FILES['file']['tmp_name']);
        // assign error message, remove uploaded file, redisplay form.
      } else {
       //File has passed all validation, copy it to the final destination and remove the temporary file:
       copy($HTTP_POST_FILES['file']['tmp_name'],"/finallocation/".$HTTP_POST_FILES['file']['name']);
       unlink($HTTP_POST_FILES['file']['tmp_name']);
       print "File has been successfully uploaded!";
       exit;
     }
    }
  }
}
?>

<html>
<head></head>
<body>
<form action="<?=$PHP_SELF?>" method="post" enctype="multipart/form-data">
<?=$error?>
<br><br>
Choose a file to upload:<br>
<input type="file" name="file"><br>
<input type="submit" name="submit" value="submit">
</form>
</body>
</html>

To stop the script from redisplaying the form after a successful upload has been completed, we add a message letting the user know that the file has been accepted, and exit the script. You can also easily add a header redirection command here to move to another page or perform any additional form validation.

If the upload was unsuccessful, the form will automatically redisplay. An added PHP line within the form HTML to output the error will provide details the user explaining why the file was not accepted.

When File Uploads Don't Work

 Parse errors aside, there are a few problems that can occur when working with file uploads. If you are just learning PHP, being confronted with one of these can quickly become extremely frustrating. Fortunately, most of the potential problems are not exotic and have fairly straight forward solutions:

File does not get uploaded at all, no error returned.
Assuming that the scripting is correct, this problem most frequently pops up when uploading larger files. In addition to the size limitations that you impose on the file during server-side validation, there are also several settings in the PHP.ini file that control the maximum uploaded file size. These settings will over-ride any options you have specified in the script.

If a smaller upload of a few KB succeeds while a larger one of several MB fails, it’s a good bet that this is the reason why. If you are running your own server and have access to the PHP.ini file, the maximum file size can be adjusted by changing the upload_max_filesize attribute. Additionally, if set, the memory_limit directive may be too small as well.

File does not get copied to the final destination, permission denied error.
This happens on *nix based servers when the script does not have access to write to the specified directory. Permissions on *nix servers affect who can read and write to directories and files and are divided into three groups: owner, group, other. Permissions can be set from most FTP programs or from a command line connection to the server. For directories where files are being written, permissions should be set to 666 or 777.

File does not get uploaded, or cannot be copied; errors including: "open_basedir restriction in effect", "Safe Mode Restriction in effect.", or "function has been disabled for security reasons"
This type of error is common in scripts running on shared web hosting providers and indicates that PHP is running in Safe Mode. Safe Mode allows the administrator to control which users are allowed to run which functions and also entirely disable functions for security purposes. Without access to the PHP.ini, safe mode can only be disabled by the server adminstrator. From the PHP.ini, it can be disabled via the safe_mode directive.

Other Notes on File Uploads

Because of changes in PHP over the last several major releases and differences in configuration settings, some aspects of working with file uploads are changeable. Here are a few things to keep in mind:

In PHP 4.2.0 a new element was introduced as part of the $HTTP_POST_FILES array which includes the specific error message returned should a file upload fail. Accessible as $HTTP_POST_FILES['file']['error'] it returns the following:

0: No error, the file was uploaded successfully 1: The upload is larger than the amount allowable by the upload_max_filesize directive in the php.ini
2: The upload is larger than the MAX_FILE_SIZE directive that was specified via html
3: The file was only partially uploaded
4: no file was uploaded

These messages can be particularly useful for error checking and to determine the success or failure of an upload, but because only the newest versions of PHP support them, it is not advisable to rely on their existence if you are programming scripts for distribution.

The exact variable names that are used to reference uploaded files depend on the version and configuration of PHP running. The $HTTP_POST_FILES array has been available since version 4.0, but are being favored in latest versions of PHP by the new, shorter $_FILES array. In configurations where register_globals is on, the $HTTP_POST_FILES array may not available - file uploads can still be accessed as:

$file

the temporary name assigned to the file when its uploaded.
$file_name the original name of the file from the user's computer.
$file_type the mime type of the file, as provided by the user's browser
$file_size the size of the file, in bytes.

is_uploaded_file() is not available in all versions of PHP. If you are unfortunate enough to be using one of the particular versions of PHP that does not support this function, you can still perform the check to see if a file has been uploaded or not by see if the temp name of the file is equal to "none" or empty:

if ($HTTP_POST_FILES['file']['tmp_name']=="none" OR $HTTP_POST_FILES['file']['tmp_name']="") {
  //no file uploaded
}

In addition to handling file size validation from the server side, you may also specify maximum file size using a hidden field within the form:

<input type="hidden" name="MAX_FILE_SIZE" value="1000">

This should appear before the file upload field that it affects. Keep in mind that this attribute is only a suggestion to the browser and not 100% reliable. Though useful as a first line of defense against large uploads, this should not replace server side validation.

Finally

In this article, you have gotten a taste of how to work with file uploads. In future articles, we will be returning to the topic through real-world examples. Next time, though, we'll focus on how PHP handles error reporting, and take a look at some functions and methods that can help you debug your own scripts.

Stay Tuned!

Things to Remember:

  • When creating forms that include a file upload field, you must include enctype="multipart/form-data" in the form tag to tell the browser to expect an upload and set the form's method to POST.
  • In most configurations, the uploaded file will be available in a $HTTP_POST_VARS array with the same name as the file upload field.
  • When you accept file uploads, you generally want to check 3 things on any potential incoming files:
    1. whether or not a file was uploaded
    2. the size of the uploaded file
    3. the file type of the uploaded file
  • The temporary file should be discarded using the unlink() function after the file has been copied to its final location or if the file fails one of the checks.



Liz Fulghum currently lives in Annapolis, MD where she works as a web designer for a custom shirt retailer. Liz was convinced to try PHP as an alternative to Perl; she has been a fan ever since and frequently works as a freelance developer.

# # #

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date