Skip to content

PHP Tutorial File Handling

Uploading Files

Lesson 173 of 369 2 min read By Deepak Paswan Updated Oct 5, 2026

Letting visitors upload files (a profile picture, a document) takes three parts: a form, the PHP that receives the file and careful checks. This lesson covers the basics. The next lesson makes it safe.

The form

index.html
HTML
<form action="upload.php" method="post" enctype="multipart/form-data">
  <label for="photo">Choose a photo</label>
  <input type="file" id="photo" name="photo" accept="image/png, image/jpeg">
  <button type="submit">Upload</button>
</form>

Both method="post" and enctype="multipart/form-data" are required. The accept attribute only hints to the browser. It does not enforce anything.

Receive the file

PHP saves the upload in a temporary folder and describes it in $_FILES (see $_FILES). You must move it to a permanent place with move_uploaded_file(). The temporary file is deleted when the script ends.

index.php
PHP
<?php
if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $file = $_FILES["photo"] ?? null;

    if ($file === null || $file["error"] !== UPLOAD_ERR_OK) {
        exit("Upload failed.");
    }

    $target = __DIR__ . "/../uploads/" . bin2hex(random_bytes(8)) . ".jpg";

    if (move_uploaded_file($file["tmp_name"], $target)) {
        echo "Saved.";
    }
}

move_uploaded_file() also checks that the file really came from an HTTP upload, which rename() does not.

The limits in php.ini

SettingWhat it limits
upload_max_filesizeSize of one file (for example 2M)
post_max_sizeSize of the whole request. Must be bigger than the file limit
max_file_uploadsNumber of files in one request (20 by default)
file_uploadsMust be On
memory_limitShould be enough for any processing you do

Check the current values:

index.php
PHP
<?php
echo ini_get("upload_max_filesize");
echo ini_get("post_max_size");

If a big upload leaves $_FILES and $_POST both empty, post_max_size was exceeded.

Turn the error code into a message

index.php
PHP
<?php
function uploadError(int $code): string
{
    return match ($code) {
        UPLOAD_ERR_OK => "OK",
        UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE => "The file is too large.",
        UPLOAD_ERR_PARTIAL => "The upload was interrupted. Please try again.",
        UPLOAD_ERR_NO_FILE => "Please choose a file.",
        default => "The server could not save the file.",
    };
}

echo uploadError(UPLOAD_ERR_NO_FILE);
Output
OUTPUT
Please choose a file.

Several files

Use name="photos[]" and multiple. $_FILES["photos"] then holds arrays for each key, which are awkward to loop. This helper regroups them per file.

index.php
PHP
<?php
function regroup(array $files): array
{
    $result = [];

    foreach ($files["name"] as $i => $name) {
        $result[] = [
            "name" => $name,
            "tmp_name" => $files["tmp_name"][$i],
            "error" => $files["error"][$i],
            "size" => $files["size"][$i],
        ];
    }

    return $result;
}

$_FILES = ["photos" => [
    "name" => ["a.png", "b.png"],
    "tmp_name" => ["/tmp/1", "/tmp/2"],
    "error" => [0, 0],
    "size" => [100, 200],
]];

echo count(regroup($_FILES["photos"])) . " files";
Output
OUTPUT
2 files
dangerous
This is not safe yet
Saving a file under the visitor’s chosen name, or without checking its content, is how servers get hacked. Read the next lesson before you accept uploads on a real site.