Skip to main content

PHP File Upload

Introduction

File uploading is an essential feature for many web applications. Whether you're building a social media platform that allows users to share images, a document management system, or a simple contact form with attachments, understanding how to handle file uploads in PHP is a valuable skill.

In this tutorial, we'll learn how to:

  • Create HTML forms that support file uploads
  • Process uploaded files using PHP
  • Implement validation and security checks
  • Save uploaded files to the server
  • Handle common errors and edge cases

Prerequisites

Before diving into file uploads, you should have:

  • Basic knowledge of HTML forms
  • Familiarity with PHP syntax and server-side processing
  • A web server with PHP installed

Creating a File Upload Form

To allow users to upload files, we need to create an HTML form with the following requirements:

  1. The form's enctype attribute must be set to multipart/form-data
  2. The form's method must be POST
  3. The form must include an <input type="file"> element

Here's a basic file upload form:

html
<!DOCTYPE html>
<html>
<head>
<title>File Upload Form</title>
</head>
<body>
<h2>Upload a File</h2>
<form action="upload.php" method="post" enctype="multipart/form-data">
<label for="fileToUpload">Select file:</label>
<input type="file" name="fileToUpload" id="fileToUpload">
<input type="submit" name="submit" value="Upload File">
</form>
</body>
</html>

Key Form Attributes Explained

  • enctype="multipart/form-data": This attribute is crucial for file uploads as it specifies how the form data should be encoded when submitted to the server. Without this attribute, file data won't be transmitted properly.
  • method="post": File uploads can be large, so we use POST rather than GET to avoid URL length limitations.
  • <input type="file">: This creates the file selection control that allows users to browse their device for files.

Processing File Uploads in PHP

When a user submits a form with a file, PHP stores the uploaded file information in the $_FILES superglobal array. Let's create a PHP script (upload.php) to handle the uploaded file:

php
<?php
// Check if the form was submitted
if(isset($_POST["submit"])) {
// Information about the uploaded file
$fileName = $_FILES["fileToUpload"]["name"];
$fileType = $_FILES["fileToUpload"]["type"];
$fileSize = $_FILES["fileToUpload"]["size"];
$fileTmpName = $_FILES["fileToUpload"]["tmp_name"];
$fileError = $_FILES["fileToUpload"]["error"];

// Display file information
echo "<h3>File Information:</h3>";
echo "File Name: " . $fileName . "<br>";
echo "File Type: " . $fileType . "<br>";
echo "File Size: " . ($fileSize / 1024) . " KB<br>";

// Check if there was an error during upload
if($fileError === 0) {
// Specify the directory where you want to save the uploaded file
$uploadDir = "uploads/";

// Create the directory if it doesn't exist
if(!file_exists($uploadDir)) {
mkdir($uploadDir, 0777, true);
}

// Generate the file path where the file will be saved
$filePath = $uploadDir . $fileName;

// Move the uploaded file from the temporary location to the desired location
if(move_uploaded_file($fileTmpName, $filePath)) {
echo "The file " . $fileName . " has been uploaded successfully.";
} else {
echo "Sorry, there was an error uploading your file.";
}
} else {
echo "Error: " . $fileError;
}
}
?>

Understanding the $_FILES Array

The $_FILES superglobal array contains information about the uploaded file in the following format:

$_FILES["input_name"]["property"]

Where input_name is the name attribute of the file input field, and property can be one of:

  • name: The original name of the file on the client machine
  • type: The MIME type of the file (e.g., "image/jpeg", "application/pdf")
  • size: The size of the file in bytes
  • tmp_name: The temporary filename where the uploaded file is stored on the server
  • error: An error code associated with the file upload

File Upload Errors

The error property of the $_FILES array can have the following values:

  • 0: No error, the file was uploaded successfully
  • 1: The uploaded file exceeds the upload_max_filesize directive in php.ini
  • 2: The uploaded file exceeds the MAX_FILE_SIZE directive specified in the HTML form
  • 3: The file was only partially uploaded
  • 4: No file was uploaded
  • 6: Missing a temporary folder
  • 7: Failed to write file to disk
  • 8: A PHP extension stopped the file upload

Validating File Uploads

For security and functionality reasons, you should always validate uploaded files. Let's enhance our script with validation:

php
<?php
// Check if the form was submitted
if(isset($_POST["submit"])) {
// Information about the uploaded file
$fileName = $_FILES["fileToUpload"]["name"];
$fileType = $_FILES["fileToUpload"]["type"];
$fileSize = $_FILES["fileToUpload"]["size"];
$fileTmpName = $_FILES["fileToUpload"]["tmp_name"];
$fileError = $_FILES["fileToUpload"]["error"];

// If no error occurred
if($fileError === 0) {
// Get file extension
$fileExt = strtolower(pathinfo($fileName, PATHINFO_EXTENSION));

// List of allowed file extensions
$allowedExts = array("jpg", "jpeg", "png", "pdf", "doc", "docx");

// Check if the file extension is allowed
if(in_array($fileExt, $allowedExts)) {
// Check file size (limit to 2MB)
if($fileSize < 2000000) {
// Generate a unique filename to prevent overwriting
$newFileName = uniqid('file_', true) . '.' . $fileExt;
$uploadDir = "uploads/";

// Create directory if it doesn't exist
if(!file_exists($uploadDir)) {
mkdir($uploadDir, 0777, true);
}

$filePath = $uploadDir . $newFileName;

// Move the uploaded file
if(move_uploaded_file($fileTmpName, $filePath)) {
echo "File uploaded successfully. Saved as: " . $newFileName;
} else {
echo "Error: Failed to move uploaded file.";
}
} else {
echo "Error: File size exceeds the limit of 2MB.";
}
} else {
echo "Error: Only JPG, JPEG, PNG, PDF, DOC, and DOCX files are allowed.";
}
} else {
// Handle specific upload errors
switch($fileError) {
case 1:
echo "Error: The uploaded file exceeds the upload_max_filesize directive in php.ini.";
break;
case 2:
echo "Error: The uploaded file exceeds the MAX_FILE_SIZE directive in the HTML form.";
break;
case 3:
echo "Error: The uploaded file was only partially uploaded.";
break;
case 4:
echo "Error: No file was uploaded.";
break;
case 6:
echo "Error: Missing a temporary folder.";
break;
case 7:
echo "Error: Failed to write file to disk.";
break;
case 8:
echo "Error: A PHP extension stopped the file upload.";
break;
default:
echo "Error: Unknown upload error.";
}
}
}
?>

Security Considerations

File uploads can pose security risks if not handled properly. Here are some key security considerations:

  1. Validate file types: Only allow specific file extensions and MIME types.
  2. Check file size: Limit the maximum file size to prevent server overload.
  3. Generate unique filenames: Prevent file overwriting and predictable file paths.
  4. Store files outside the web root: If possible, store uploaded files in a directory that isn't directly accessible via a URL.
  5. Scan for malicious content: For high-security applications, consider implementing virus scanning.
  6. Set proper permissions: Ensure uploaded files have appropriate permissions.

The Upload Process Diagram

Multiple File Uploads

To allow users to upload multiple files at once, modify your HTML form by adding the multiple attribute to the file input:

html
<input type="file" name="files[]" id="fileToUpload" multiple>

Note the name attribute now uses files[] syntax to tell PHP to treat it as an array. Here's how to handle multiple file uploads in PHP:

php
<?php
if(isset($_POST["submit"])) {
// Count the number of uploaded files
$totalFiles = count($_FILES["files"]["name"]);

// Loop through each file
for($i = 0; $i < $totalFiles; $i++) {
$fileName = $_FILES["files"]["name"][$i];
$fileSize = $_FILES["files"]["size"][$i];
$fileTmpName = $_FILES["files"]["tmp_name"][$i];
$fileError = $_FILES["files"]["error"][$i];

// Process each file (use validation code from previous examples)
if($fileError === 0) {
// Your validation and upload code here
$uploadDir = "uploads/";

if(!file_exists($uploadDir)) {
mkdir($uploadDir, 0777, true);
}

$filePath = $uploadDir . $fileName;

if(move_uploaded_file($fileTmpName, $filePath)) {
echo "File " . $fileName . " uploaded successfully.<br>";
} else {
echo "Error uploading " . $fileName . ".<br>";
}
} else {
echo "Error with file " . $fileName . ": " . $fileError . "<br>";
}
}
}
?>

Practical Example: Image Upload with Preview

Here's a more practical example of an image upload form with client-side preview:

html
<!DOCTYPE html>
<html>
<head>
<title>Image Upload with Preview</title>
<style>
#preview {
max-width: 300px;
max-height: 300px;
margin-top: 20px;
display: none;
}
</style>
</head>
<body>
<h2>Upload an Image</h2>
<form action="image_upload.php" method="post" enctype="multipart/form-data">
<label for="imageFile">Select image:</label>
<input type="file" name="imageFile" id="imageFile" accept="image/*">
<div>
<img id="preview" src="#" alt="Preview">
</div>
<input type="submit" name="submit" value="Upload Image">
</form>

<script>
// Show image preview
document.getElementById('imageFile').addEventListener('change', function() {
var preview = document.getElementById('preview');
var file = this.files[0];
var reader = new FileReader();

reader.onload = function(e) {
preview.src = e.target.result;
preview.style.display = 'block';
}

if(file) {
reader.readAsDataURL(file);
}
});
</script>
</body>
</html>

And the corresponding PHP processing file (image_upload.php):

php
<?php
if(isset($_POST["submit"])) {
$file = $_FILES["imageFile"];

// Check if it's an image
$check = getimagesize($file["tmp_name"]);
if($check !== false) {
// It's an image
$uploadDir = "uploads/images/";

if(!file_exists($uploadDir)) {
mkdir($uploadDir, 0777, true);
}

// Generate unique filename
$fileExt = strtolower(pathinfo($file["name"], PATHINFO_EXTENSION));
$newFileName = uniqid('img_', true) . '.' . $fileExt;
$filePath = $uploadDir . $newFileName;

// Allowed image formats
$allowedFormats = array("jpg", "jpeg", "png", "gif");

if(in_array($fileExt, $allowedFormats)) {
if($file["size"] < 5000000) { // 5MB limit
if(move_uploaded_file($file["tmp_name"], $filePath)) {
echo "
<h2>Image Uploaded Successfully</h2>
<p>File saved as: " . $newFileName . "</p>
<div><img src='" . $filePath . "' style='max-width: 500px;'></div>
<p><a href='image_upload_form.html'>Upload another image</a></p>
";
} else {
echo "Error: Failed to move uploaded file.";
}
} else {
echo "Error: File size exceeds the limit of 5MB.";
}
} else {
echo "Error: Only JPG, JPEG, PNG, and GIF files are allowed.";
}
} else {
echo "Error: Uploaded file is not an image.";
}
}
?>

Configuring PHP for File Uploads

PHP has several configuration directives in the php.ini file that affect file uploads:

  • file_uploads: Set to On to enable file uploads
  • upload_max_filesize: Maximum size of an uploaded file
  • post_max_size: Maximum size of POST data (should be larger than upload_max_filesize)
  • max_file_uploads: Maximum number of files that can be uploaded simultaneously
  • upload_tmp_dir: Temporary directory for file uploads

You can check your current PHP configuration using this code:

php
<?php
echo "file_uploads: " . ini_get('file_uploads') . "<br>";
echo "upload_max_filesize: " . ini_get('upload_max_filesize') . "<br>";
echo "post_max_size: " . ini_get('post_max_size') . "<br>";
echo "max_file_uploads: " . ini_get('max_file_uploads') . "<br>";
echo "upload_tmp_dir: " . ini_get('upload_tmp_dir') . "<br>";
?>

Handling Large File Uploads

For large file uploads (like videos or large documents), you may need to:

  1. Adjust your PHP configuration to allow larger uploads
  2. Implement a progress bar to give feedback to users
  3. Consider using chunked uploads for very large files

Here's an example of creating a simple progress bar using JavaScript and PHP:

html
<!DOCTYPE html>
<html>
<head>
<title>File Upload with Progress Bar</title>
<style>
.progress {
width: 100%;
background-color: #f0f0f0;
border-radius: 4px;
margin: 10px 0;
}
.progress-bar {
height: 20px;
background-color: #4CAF50;
width: 0%;
border-radius: 4px;
text-align: center;
line-height: 20px;
color: white;
}
</style>
</head>
<body>
<h2>Upload a Large File</h2>
<form id="uploadForm" enctype="multipart/form-data">
<input type="file" name="largeFile" id="largeFile">
<div class="progress">
<div class="progress-bar" id="progressBar">0%</div>
</div>
<button type="submit">Upload</button>
</form>

<div id="status"></div>

<script>
document.getElementById('uploadForm').addEventListener('submit', function(e) {
e.preventDefault();

var formData = new FormData();
var fileInput = document.getElementById('largeFile');
var file = fileInput.files[0];
formData.append('largeFile', file);

var xhr = new XMLHttpRequest();

// Progress event
xhr.upload.addEventListener('progress', function(e) {
if (e.lengthComputable) {
var percentage = Math.round((e.loaded / e.total) * 100);
document.getElementById('progressBar').style.width = percentage + '%';
document.getElementById('progressBar').textContent = percentage + '%';
}
});

// Load completed
xhr.addEventListener('load', function() {
document.getElementById('status').innerHTML = xhr.responseText;
});

// Send file
xhr.open('POST', 'process_large_upload.php', true);
xhr.send(formData);
});
</script>
</body>
</html>

The PHP handler (process_large_upload.php):

php
<?php
// Increase max execution time for large uploads
ini_set('max_execution_time', 300); // 5 minutes

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_FILES['largeFile'])) {
$file = $_FILES['largeFile'];

if ($file['error'] === 0) {
$uploadDir = "uploads/large/";

if (!file_exists($uploadDir)) {
mkdir($uploadDir, 0777, true);
}

$fileName = $file['name'];
$filePath = $uploadDir . $fileName;

if (move_uploaded_file($file['tmp_name'], $filePath)) {
echo "<p>File uploaded successfully!</p>";
echo "<p>File name: " . $fileName . "</p>";
echo "<p>File size: " . round($file['size'] / 1048576, 2) . " MB</p>";
} else {
echo "<p>Error: Failed to move the uploaded file.</p>";
}
} else {
echo "<p>Error code: " . $file['error'] . "</p>";
}
} else {
echo "<p>No file was uploaded.</p>";
}
}
?>

Summary

In this tutorial, we've covered:

  1. How to create HTML forms for file uploads
  2. Processing uploaded files in PHP using the $_FILES superglobal
  3. Validating file uploads for security and functionality
  4. Saving uploaded files to the server
  5. Handling multiple file uploads
  6. Creating a practical image upload system with preview
  7. Managing PHP configuration for file uploads
  8. Implementing a progress bar for large file uploads

File uploads are a powerful feature that allows users to interact with your web applications in a meaningful way. By implementing proper validation and security measures, you can create robust file upload systems that enhance user experience while keeping your application secure.

Exercises

  1. Create a profile picture upload system that resizes the uploaded image to a square thumbnail.
  2. Build a document management system that allows users to upload, list, and download files.
  3. Implement a file type verification system that checks both the file extension and the actual file content (using functions like finfo_file()).
  4. Create a drag-and-drop file upload interface using HTML5 and JavaScript.
  5. Build a file upload system that integrates with a database to store metadata about uploaded files.

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)