Languages PHP Uploading Files within a PHP Script

Uploading Files within a PHP Script

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.

# # #

Latest Posts

Related Stories