Categories
PHP

Handling File Uploads

Learn how to upload files through an HTML form and save them somewhere on the server.

You’ll learn about the following:

  1. HTML Form Requirements for File Uploads
  2. PHP Configuration for File Uploads
  3. PHP $_FILES Array
    PHP File Upload Error Codes
  4. Moving Uploaded Files to a Safe Location
  5. Understanding File Upload Errors

Example: Simple File Uploading PHP Script

<?php
 //$file = isset($_FILES['upload']) ? $_FILES['upload'] : array();
 $file = $_FILES['upload'] ?? [];

 if ( ! empty($file) && $file['error'] == 0 ) {
  echo 'name: ' . $file['name'] . '<br>';
  echo 'type: ' . $file['type'] . '<br>';
  echo 'size: ' . $file['size'] . '<br>';
  echo 'error: ' . $file['error'] . '<br>';
  echo 'tmp_name: ' . $file['tmp_name'] . '<br>';
  echo 'full_path: ' . $file['full_path'] . '<br>';
  }

?>
<form action="example.php" method="post" enctype="multipart/form-data">
 <input type="file" name="upload" >
 <input type="submit" name="submit" value="Upload">
</form>

HTML Form Requirements

The HTML form has to fulfill two requirements when uploading files to the web server:

  • The enctype (encoding type) attribute has to be set to "multipart/form-data"
  • The method attribute has to be set to "post"

Without these settings, the file upload does not work. Next, we need to add an input tag and set the value of its type attribute to file.

Adding a file upload field to a form:

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

Limiting Upload File Size in Form with MAX_FILE_SIZE

To restrict the upload file size add a hidden input field, set the name attribute value to MAX_FILE_SIZE and specify a maximum size (in bytes) for an upload file in value attribute:

<form action="example.php" method="post" enctype="multipart/form-data">

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

 <input type="file" name="upload">
 <input type="submit" name="submit" value="Upload">
</form>

Note: The MAX_FILE_SIZE filed must place before the file input field, otherwise it won’t work.

PHP File Uploads Configuration

You can check whether your server supports file uploads by running phpinfo() on your server. To do this, create a PHP file and add the following code:

<?php
 //info.php
 echo phpinfo();

Scroll down to the Core section, and find the file_uploads directive, if the Local Value is On, your server allowed the file uploads. If Off, uploads have been disabled.

The following figure shows the important directives related to file uploads:

  1. file_uploads
    Whether or not to allow HTTP file uploads. If the Local Value is On, your server configuration allows file uploads.
  2. max_execution_time:
    The maximum time in seconds a script is allowed to run before it is terminated by the parser. For large files upload, the script may take a longer time (depending on the user’s internet speed). If the script takes longer, PHP generates a fatal error.
  3. max_file_uploads:
    The maximum number of files allowed to be uploaded simultaneously.
  4. max_input_time:
    The maximum time in seconds a script is allowed to parse input data (i.e. POST and GET). Uploading very large files may run out of time.
  5. post_max_size:
    The maximum allowed size (in bytes) of post data. To upload large files, this value must be larger than upload_max_filesize and memory_limit should be larger than this value.
  6. upload_max_filesize:
    The maximum size of an uploaded file. The post_max_size must be larger than this value.
  7. upload_tmp_dir:
    A writable directory for temporarily storing uploaded files.

Understanding the $_FILES array

You can’t refer to an uploaded file in the $_POST array in the same way you do with other form fields. PHP uses a separate superglobal array $_FILES to retrieve the details of uploaded files. You can find the file upload form field under its name:

<?php
 if ($_SERVER['REQUEST_METHOD'] == 'POST') {
  echo '<pre>';
  print_r($_FILES);
  echo '</pre>';
 }
 ?>
<form action="example.php" method="post" enctype="multipart/form-data">
 <input type="file" name="upload">
 <input type="submit" name="submit" value="Upload">
</form>

The following subkeys provide the information about the uploaded file:

  • $_FILES["upload"]["name"]
    Original filename sent by the browser. Do not rely on the "name" because this information can be forged. Also, some browsers do not always send the original filename, but the complete path to it. Therefore, always call basename() to extract the filename only: basename($_FILES['upload']['name']).
  • $_FILES["upload"]["full_path"] (PHP 8.1)
    Contains the full path of the uploaded file. This is intended for use in conjunction with webkitdirectory (for the entire directory uploads).
  • $_FILES["upload"]["type"]
    The file’s MIME type as sent by the client, not reliable
  • $_FILES["upload"]["tmp_name"]
    Temporary filename where PHP saved the uploaded file. PHP deletes the file when script execution is completed, so you need to save the file immediately by using the move_uploaded_file function (discuss later).
  • $_FILES["upload"]["size"]
    Size of the file in bytes.
  • $_FILES["upload"]["error"]
    Status of upload, Error code 0 (or constant UPLOAD_ERR_OK) means the upload was successful.

PHP File Upload Error Codes

  • UPLOAD_ERR_OK
    Value: 0, indicates successful upload
  • UPLOAD_ERR_INI_SIZE
    Value: 1, file exceeds the maximum upload size specified in php.ini (default 2 MB).
  • UPLOAD_ERR_FORM_SIZE
    Value: 2, file exceeds the size specified by MAX_FILE_SIZE.
  • UPLOAD_ERR_PARTIAL
    Value: 3, file only partially uploaded.
  • UPLOAD_ERR_NO_FILE
    Value: 4, form submitted with no file specified.
  • UPLOAD_ERR_NO_TMP_DIR
    Value: 6, no temporary folder defined.
  • UPLOAD_ERR_CANT_WRITE
    Value: 7, cannot write the file to disk.
  • UPLOAD_ERR_EXTENSION
    Value: 8, upload stopped by an unspecified PHP extension.

Example:

<?php
 if ( isset($_FILES['upload']) ) {
  switch ( $_FILES['upload']['error'] ) {
   case UPLOAD_ERR_OK:
    echo 'File successfully uploaded';
    //code to move file
    break;
   case UPLOAD_ERR_INI_SIZE:
    echo 'File exceeds the maximum upload size specified in php.ini (default 2 MB)';
    break;
   case UPLOAD_ERR_FORM_SIZE:
    echo 'File exceeds the size specified by MAX_FILE_SIZE';
    break;
   case UPLOAD_ERR_PARTIAL:
    echo 'File only partially uploaded';
    break;
   case UPLOAD_ERR_NO_FILE:
    echo 'Form submitted with no file specified';
    break;
   case UPLOAD_ERR_NO_TMP_DIR:
    echo 'No temporary folder defined';
    break;
   case UPLOAD_ERR_CANT_WRITE:
    echo 'Cannot write the file to disk';
    break;
   case UPLOAD_ERR_EXTENSION:
    echo 'Upload stopped by an unspecified PHP extension';
  }
 }

?>
<form action="example.php" method="post" enctype="multipart/form-data">
 <input type="hidden" name="MAX_FILE_SIZE" value="15360">
 <input type="file" name="upload" >
 <input type="submit" name="submit" value="Upload">
</form>

Moving Uploaded Files to a Safe Location

<?php
 //Syntax
 move_uploaded_file(string $from, string $to): bool

When a user uploads a file PHP stores the file in a temporary location (set in the php.ini directive upload_tmp_dir) and deletes it upon completion of script execution. Therefore, you have to move (or save) the uploaded file within the script.

move_uploaded_file function

You must use the PHP function move_uploaded_file() to move the uploaded file to another location. The move_uploaded_file() function first does a sanity check, whether the filename you provide really is an uploaded file or if a malicious user just tried to trick you into moving /etc/passwd or C:\boot.ini somewhere else.

is_uploaded_file function

The is_uploaded_file function tests whether the file was uploaded via HTTP POST. It also test whether the filename you provide really is an uploaded file or if a malicious user just tried to trick you into moving /etc/passwd or C:\boot.ini somewhere else.

Moving an Uploaded File to a New Location

<?php
 if (isset($_FILES['upload']) &&
     $_FILES['upload']['error'] == UPLOAD_ERR_OK &&
     is_uploaded_file($_FILES['upload'][tmp_name']) ) {
  $from = $_FILES['upload']['tmp_name'];
  $to   = 'uploads'.DIRECTORY_SEPARATOR
                   .basename($_FILES['upload']['name']);

  if (move_uploaded_file($from, $to))
   echo 'File moved';
  else
   echo 'File not moved';
 }
?>
<form action="example.php" method="post" enctype="multipart/form-data">
 <input type="file" name="upload" multiple>
 <input type="submit" name="submit" value="Upload">
</form>

Note: Different OSs use different directory separators, Windows uses \ and Linux uses /. It is good practice to write DIRECTORY_SEPARATOR constant instead of writing the / or \ in your PHP scripts.

Common Errors

Why is permission denied?

Warning: move_uploaded_file(uploads/readme.txt): Failed to open stream: Permission denied in...

The directory (or folder) where you trying to move the uploaded file has no writeable permission. If your website is hosted on a Linux server you need to give global access (chmod 777) to the directory to which you want to upload files. You can use an FTP client to configure permissions of a directory if you intend to make files available publically to your website visitors.

Failed to open stream or Unable to move:

Warning: move_uploaded_file(uploads/readme.txt): Failed to open stream: No such file or directory in...

Warning: move_uploaded_file(): Unable to move "D:\xampp\tmp\php5266.tmp" to "uploads/readme.txt" in ...

The above errors occurred when the directory where you are trying to move the file was not found. Check if you’ve written the correct path in the script.


Processing Forms in PHP: