Skip to main content
PHP for Beginners
CHAPTER 24 Beginner

PHP File Upload System

Updated: May 12, 2026
30 min read

# Chapter 24: PHP File Upload System

1. Introduction

Welcome to Chapter 24! A modern web application allows users to upload content—profile pictures, PDF resumes, or video files. Handling file uploads in PHP is a critical skill, but it is also one of the most dangerous. If you allow users to upload files without strict validation, a hacker could upload a malicious .php script and take over your entire server. In this chapter, we will learn how to process file uploads using the $_FILES superglobal, validate file types and sizes securely, and save them to a designated folder.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Structure an HTML form to accept file uploads.
  • Understand the $_FILES superglobal array structure.
  • Move an uploaded file from a temporary folder to a permanent folder.
  • Validate file extensions (e.g., only allow .jpg or .png).
  • Validate file sizes.
  • Handle upload errors gracefully.

3. The Upload Form

To upload files, your HTML <form> must use method="POST" and it must include the enctype="multipart/form-data" attribute. Without enctype, the file data will simply not be sent to the server.
html
12345
<form action="upload.php" method="POST" enctype="multipart/form-data">
    <label>Select Profile Image:</label>
    <input type="file" name="profile_pic" required>
    <button type="submit">Upload Image</button>
</form>

4. Understanding $_FILES

When the form is submitted, PHP temporarily saves the file in a hidden server directory and populates the $_FILES array. If your input name is profile_pic, $_FILES['profile_pic'] will contain an associative array with 5 pieces of information:
  • name: The original name of the file (e.g., vacation.jpg).
  • type: The MIME type of the file (e.g., image/jpeg).
  • tmp_name: The temporary file path on the server (e.g., C:\xampp\tmp\phpABCD.tmp).
  • error: An error code (0 means success).
  • size: The size of the file in bytes.

5. Basic Upload Processing (The Unsafe Way)

To save the file permanently, we must use the move_uploaded_file() function to move it from its temporary location (tmp_name) to a folder we control (like an uploads/ folder).
php
1234567891011121314
<?php
// upload.php (Basic - Do not use in production!)
if ($_SERVER[&#039;REQUEST_METHOD'] == 'POST') {
    $temp_location = $_FILES[&#039;profile_pic']['tmp_name'];
    $target_location = "uploads/" . $_FILES[&#039;profile_pic']['name'];

    // Move the file
    if (move_uploaded_file($temp_location, $target_location)) {
        echo "File uploaded successfully!";
    } else {
        echo "Upload failed.";
    }
}
?>

*Note: You must manually create the uploads folder in your project directory before running this!*

6. The Danger: Why Validation is Mandatory

If a hacker uploads a file named hack.php containing malicious code, the basic script above will gladly save it to uploads/hack.php. The hacker can then visit yourwebsite.com/uploads/hack.php in their browser, executing the code and taking control of your server. We MUST strictly validate the file extension!

7. Secure Validation Strategy

We will validate three things:
  1. 1. Ensure the file has no errors (error == 0).
  1. 2. Ensure the file is not too large (e.g., < 2MB).
  1. 3. Ensure the file extension is strictly allowed (e.g., only jpg, png).
php
1234567891011121314151617181920212223242526272829303132333435
<?php
if ($_SERVER[&#039;REQUEST_METHOD'] == 'POST' && isset($_FILES['profile_pic'])) {
    
    $file = $_FILES[&#039;profile_pic'];
    $max_size = 2 * 1024 * 1024; // 2MB in bytes
    
    // 1. Get the actual file extension
    $file_extension = strtolower(pathinfo($file[&#039;name'], PATHINFO_EXTENSION));
    
    // 2. Define an array of allowed extensions
    $allowed_extensions = [&#039;jpg', 'jpeg', 'png', 'gif'];

    // Validation Checks
    if ($file[&#039;error'] !== 0) {
        die("There was an error uploading your file.");
    }
    
    if ($file[&#039;size'] > $max_size) {
        die("File is too large. Max 2MB.");
    }
    
    if (!in_array($file_extension, $allowed_extensions)) {
        die("Invalid file type. Only JPG, PNG, and GIF are allowed.");
    }

    // Generate a unique name to prevent overwriting files with the same name
    $new_filename = uniqid("img_", true) . "." . $file_extension;
    $target = "uploads/" . $new_filename;

    // Finally, move the file safely
    if (move_uploaded_file($file[&#039;tmp_name'], $target)) {
        echo "Secure upload successful! Saved as $new_filename";
    }
}
?>

8. Output Explanations

In the secure script, pathinfo() extracts just the extension (.jpg). We check if that extension exists inside our $allowed_extensions array using in_array(). If someone tries to upload .php, the script dies immediately. Furthermore, we use uniqid() to generate a completely random string for the new filename (e.g., img_60f3b4c123.jpg). This ensures that if two users upload an image named avatar.jpg, the second one doesn't overwrite the first one on the server!

9. Common Mistakes

  • Forgetting enctype="multipart/form-data": The form will submit, but $_FILES will be empty.
  • Trusting the type array key: $_FILES['profile_pic']['type'] is sent by the browser. A hacker can easily fake this to say image/jpeg while uploading a .php file. Never rely on the MIME type alone for security; always verify the extension using pathinfo().
  • Directory Permissions: On live Linux servers, PHP might not have permission to write to your uploads/ folder. You may need to CHMOD the folder to 755 or 775.

10. Best Practices

  • Never keep the original uploaded filename. Always generate a random, unique name using uniqid().
  • Never store images in the database. Store the file in a folder, and save the *string path* (e.g., uploads/img_123.jpg) in the database.
  • Disable PHP execution in your uploads/ directory via server configuration (.htaccess) as an ultimate fallback security measure.

11. Exercises

  1. 1. Create a folder named uploads in your root directory.
  1. 2. Build the secure upload form and PHP script from Section 7.
  1. 3. Attempt to upload a .txt file and verify the security blocks it.
  1. 4. Upload a .jpg and verify it appears in the uploads folder with a unique name.

12. Mini Project: Profile Image Uploader

Task: Build a page that allows a user to upload an image. Once uploaded securely, save the path to a session, and immediately display the uploaded image dynamically on the page using an <img> tag.
php
12345678910111213141516171819202122232425262728293031323334353637383940414243
<?php
session_start();

if ($_SERVER[&#039;REQUEST_METHOD'] == 'POST' && isset($_FILES['avatar'])) {
    $file = $_FILES[&#039;avatar'];
    $ext = strtolower(pathinfo($file[&#039;name'], PATHINFO_EXTENSION));
    $allowed = [&#039;jpg', 'jpeg', 'png'];

    if ($file[&#039;error'] === 0 && in_array($ext, $allowed) && $file['size'] < 2000000) {
        $new_name = uniqid() . "." . $ext;
        $target_dir = "uploads/" . $new_name;
        
        if (move_uploaded_file($file[&#039;tmp_name'], $target_dir)) {
            // Save path to session
            $_SESSION[&#039;avatar_path'] = $target_dir;
            $msg = "Upload successful!";
        }
    } else {
        $msg = "Upload failed. Invalid file.";
    }
}
?>

<!DOCTYPE html>
<html>
<body>
    <h2>Update Profile Picture</h2>
    <p><?php echo isset($msg) ? $msg : &#039;'; ?></p>
    
    <form method="POST" enctype="multipart/form-data">
        <input type="file" name="avatar" required>
        <button type="submit">Upload</button>
    </form>

    <hr>
    
    <?php if(isset($_SESSION[&#039;avatar_path'])): ?>
        <h3>Current Avatar:</h3>
        <!-- Displaying the image dynamically -->
        <img src="<?php echo $_SESSION[&#039;avatar_path']; ?>" width="200" style="border-radius: 50%;">
    <?php endif; ?>
</body>
</html>

13. Coding Challenges

Challenge 1: Modify the validation script. Add a check to ensure the file is an actual image, not a fake file renamed to .jpg. Research and use the PHP getimagesize() function to accomplish this.

14. MCQs with Answers

1. What attribute is strictly required on an HTML form to upload files? A) method="FILE" B) upload="enabled" C) enctype="multipart/form-data" D) data-type="file" *Answer: C*

2. Where does PHP store an uploaded file immediately after submission? A) In the database. B) In a temporary system directory defined by the server. C) In the uploads folder. D) In the $_SESSION array. *Answer: B*

3. Why should you generate a random unique name for uploaded files? A) To make the file download faster. B) To encrypt the image. C) To prevent users from guessing file URLs and to prevent overwriting files that happen to have the same original name. D) Because PHP cannot read original filenames. *Answer: C*

15. Interview Questions

Q: Explain the process of securely handling a file upload in PHP. *A:* First, ensure the form has enctype="multipart/form-data". Upon submission, check $_FILES['file']['error'] for success. Extract the file extension using pathinfo() and check it against a strict whitelist array (e.g., ['jpg', 'png']). Validate the file size. If all checks pass, generate a secure, unique filename using uniqid(), and use move_uploaded_file() to transfer it from the temporary directory to the final destination.

Q: Should you store images as BLOBs in the database, or in a folder? *A:* While databases support BLOB (Binary Large Object) data, it is almost universally considered best practice to store files in a folder on the server's hard drive. Storing large files in the database massively bloats the database size, slows down regular text queries, and makes backups extremely difficult. Store the file in a folder, and store the text path (/uploads/file.jpg) in the database.

16. FAQs

Q: My upload script works for small images, but fails silently for large 5MB images. Why? *A:* PHP has a built-in configuration limit for file uploads (usually set to 2MB by default in php.ini). If a file exceeds upload_max_filesize or post_max_size, PHP drops the file before your script even runs. You must edit your server's php.ini file to increase these limits.

17. Summary

You are now capable of handling complex multimedia data! You learned how to configure HTML forms for file transfer, navigate the multidimensional $_FILES array, securely validate file extensions and sizes, generate collision-proof filenames, and move temporary files into permanent storage directories safely.

18. Next Chapter Recommendation

We've touched on security frequently: SQL Injection, Hashing, XSS, and File Upload vulnerabilities. It is time to consolidate this knowledge. In Chapter 25: PHP Security Best Practices, we will take a deep dive into securing your PHP applications against the most common web attacks!

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·