PHP Fundamentals
File Uploads
File Uploads
PHP makes it easy to handle file uploads from users. This is essential for applications that need profile pictures, document uploads, media files, and more.
Security Alert: File uploads are a major security risk if not handled properly. Always validate file type, size, and content. Never trust user input!
The $_FILES Superglobal
When files are uploaded, they are stored in the $_FILES superglobal array with the following structure:
<?php
// $_FILES structure for a file input named "photo"
$_FILES['photo']['name'] // Original filename
$_FILES['photo']['type'] // MIME type (e.g., "image/jpeg")
$_FILES['photo']['size'] // File size in bytes
$_FILES['photo']['tmp_name'] // Temporary location on server
$_FILES['photo']['error'] // Error code (0 = no error)
?>
Basic Upload Form
Forms that upload files must include the enctype attribute:
<!DOCTYPE html>
<html>
<head>
<title>File Upload</title>
</head>
<body>
<h1>Upload a File</h1>
<!-- IMPORTANT: enctype="multipart/form-data" is required -->
<form method="post" action="upload.php" enctype="multipart/form-data">
<label for="file">Choose file:</label>
<input type="file" id="file" name="photo">
<button type="submit">Upload</button>
</form>
</body>
</html>
Critical: Always include
enctype="multipart/form-data" in forms with file uploads, or the files will not be sent.
Basic File Upload Handler
<?php
// upload.php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Check if file was uploaded
if (isset($_FILES['photo']) && $_FILES['photo']['error'] === 0) {
// File details
$filename = $_FILES['photo']['name'];
$temp_path = $_FILES['photo']['tmp_name'];
$file_size = $_FILES['photo']['size'];
$file_type = $_FILES['photo']['type'];
// Set upload directory
$upload_dir = 'uploads/';
$destination = $upload_dir . $filename;
// Move file from temp location to destination
if (move_uploaded_file($temp_path, $destination)) {
echo "File uploaded successfully: $filename";
} else {
echo "Error uploading file.";
}
} else {
echo "No file uploaded or an error occurred.";
}
}
?>
File Upload Error Codes
<?php
// Check upload errors
$error = $_FILES['photo']['error'];
switch ($error) {
case UPLOAD_ERR_OK:
echo "No error";
break;
case UPLOAD_ERR_INI_SIZE:
echo "File exceeds upload_max_filesize in php.ini";
break;
case UPLOAD_ERR_FORM_SIZE:
echo "File exceeds MAX_FILE_SIZE in HTML form";
break;
case UPLOAD_ERR_PARTIAL:
echo "File was only partially uploaded";
break;
case UPLOAD_ERR_NO_FILE:
echo "No file was uploaded";
break;
case UPLOAD_ERR_NO_TMP_DIR:
echo "Missing temporary folder";
break;
case UPLOAD_ERR_CANT_WRITE:
echo "Failed to write file to disk";
break;
default:
echo "Unknown error";
}
?>
Secure File Upload with Validation
<?php
// secure_upload.php
$errors = [];
$success = false;
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_FILES['photo']) && $_FILES['photo']['error'] === 0) {
$file = $_FILES['photo'];
// Get file info
$filename = $file['name'];
$temp_path = $file['tmp_name'];
$file_size = $file['size'];
$file_type = $file['type'];
// Get file extension
$file_ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
// 1. Validate file extension
$allowed_extensions = ['jpg', 'jpeg', 'png', 'gif'];
if (!in_array($file_ext, $allowed_extensions)) {
$errors[] = "Invalid file type. Allowed: " . implode(', ', $allowed_extensions);
}
// 2. Validate MIME type
$allowed_mime_types = ['image/jpeg', 'image/png', 'image/gif'];
if (!in_array($file_type, $allowed_mime_types)) {
$errors[] = "Invalid MIME type";
}
// 3. Validate file size (max 5MB)
$max_size = 5 * 1024 * 1024; // 5MB in bytes
if ($file_size > $max_size) {
$errors[] = "File too large. Maximum size: 5MB";
}
// 4. Validate that it's actually an image
$image_info = getimagesize($temp_path);
if ($image_info === false) {
$errors[] = "File is not a valid image";
}
// If no errors, proceed with upload
if (empty($errors)) {
// Generate unique filename to prevent overwrites
$new_filename = uniqid('img_', true) . '.' . $file_ext;
// Set upload directory
$upload_dir = 'uploads/';
// Create directory if it doesn't exist
if (!is_dir($upload_dir)) {
mkdir($upload_dir, 0755, true);
}
$destination = $upload_dir . $new_filename;
// Move uploaded file
if (move_uploaded_file($temp_path, $destination)) {
$success = true;
$uploaded_file = $new_filename;
} else {
$errors[] = "Failed to move uploaded file";
}
}
} else {
$error_code = $_FILES['photo']['error'];
if ($error_code === UPLOAD_ERR_NO_FILE) {
$errors[] = "Please select a file";
} else {
$errors[] = "Upload error code: $error_code";
}
}
}
?>
<!DOCTYPE html>
<html>
<head>
<title>Secure File Upload</title>
<style>
.error { color: red; }
.success { color: green; }
img { max-width: 300px; margin-top: 10px; }
</style>
</head>
<body>
<h1>Upload Image</h1>
<?php if (!empty($errors)): ?>
<div class="error">
<h3>Errors:</h3>
<ul>
<?php foreach ($errors as $error): ?>
<li><?php echo $error; ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<?php if ($success): ?>
<div class="success">
<h3>Upload Successful!</h3>
<p>Filename: <?php echo htmlspecialchars($uploaded_file); ?></p>
<img src="uploads/<?php echo htmlspecialchars($uploaded_file); ?>" alt="Uploaded image">
</div>
<?php endif; ?>
<form method="post" action="" enctype="multipart/form-data">
<label for="photo">Choose image (JPG, PNG, GIF - Max 5MB):</label>
<input type="file" id="photo" name="photo" accept="image/*">
<button type="submit">Upload</button>
</form>
</body>
</html>
Multiple File Uploads
<!-- HTML Form for Multiple Files -->
<form method="post" action="upload_multiple.php" enctype="multipart/form-data">
<label>Choose multiple images:</label>
<input type="file" name="photos[]" multiple accept="image/*">
<button type="submit">Upload All</button>
</form>
<?php
// upload_multiple.php
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
if (isset($_FILES['photos'])) {
$upload_dir = 'uploads/';
$uploaded_files = [];
$errors = [];
// Count number of files
$file_count = count($_FILES['photos']['name']);
// Loop through each file
for ($i = 0; $i < $file_count; $i++) {
// Check if file has no error
if ($_FILES['photos']['error'][$i] === 0) {
$filename = $_FILES['photos']['name'][$i];
$temp_path = $_FILES['photos']['tmp_name'][$i];
$file_size = $_FILES['photos']['size'][$i];
$file_ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
$allowed = ['jpg', 'jpeg', 'png', 'gif'];
// Validate
if (!in_array($file_ext, $allowed)) {
$errors[] = "$filename: Invalid file type";
continue;
}
if ($file_size > 5 * 1024 * 1024) {
$errors[] = "$filename: File too large";
continue;
}
// Generate unique filename
$new_filename = uniqid('img_', true) . '.' . $file_ext;
$destination = $upload_dir . $new_filename;
// Upload file
if (move_uploaded_file($temp_path, $destination)) {
$uploaded_files[] = $new_filename;
} else {
$errors[] = "$filename: Upload failed";
}
}
}
echo "<h3>Uploaded " . count($uploaded_files) . " files</h3>";
foreach ($uploaded_files as $file) {
echo "<p>$file</p>";
}
if (!empty($errors)) {
echo "<h3>Errors:</h3>";
foreach ($errors as $error) {
echo "<p style='color:red;'>$error</p>";
}
}
}
}
?>
File Upload Helper Functions
<?php
// Helper function to format file size
function formatFileSize($bytes) {
if ($bytes >= 1073741824) {
return number_format($bytes / 1073741824, 2) . ' GB';
} elseif ($bytes >= 1048576) {
return number_format($bytes / 1048576, 2) . ' MB';
} elseif ($bytes >= 1024) {
return number_format($bytes / 1024, 2) . ' KB';
} else {
return $bytes . ' bytes';
}
}
// Helper function to generate safe filename
function generateSafeFilename($original_name) {
$ext = strtolower(pathinfo($original_name, PATHINFO_EXTENSION));
return uniqid('file_', true) . '.' . $ext;
}
// Helper function to validate image
function validateImage($file) {
$errors = [];
// Check if file exists
if (!isset($file) || $file['error'] !== 0) {
$errors[] = "File upload error";
return $errors;
}
// Validate extension
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
$allowed = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
if (!in_array($ext, $allowed)) {
$errors[] = "Invalid file extension";
}
// Validate MIME type
$allowed_mime = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
if (!in_array($file['type'], $allowed_mime)) {
$errors[] = "Invalid MIME type";
}
// Validate size (5MB)
if ($file['size'] > 5 * 1024 * 1024) {
$errors[] = "File too large (max 5MB)";
}
// Validate it's a real image
if (getimagesize($file['tmp_name']) === false) {
$errors[] = "File is not a valid image";
}
return $errors;
}
// Usage
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$errors = validateImage($_FILES['photo']);
if (empty($errors)) {
$new_filename = generateSafeFilename($_FILES['photo']['name']);
$destination = 'uploads/' . $new_filename;
if (move_uploaded_file($_FILES['photo']['tmp_name'], $destination)) {
echo "Uploaded: $new_filename (" . formatFileSize($_FILES['photo']['size']) . ")";
}
} else {
foreach ($errors as $error) {
echo "<p style='color:red;'>$error</p>";
}
}
}
?>
Image Manipulation (Resize)
<?php
// Function to resize uploaded image
function resizeImage($source, $destination, $max_width, $max_height) {
// Get original dimensions
list($orig_width, $orig_height, $type) = getimagesize($source);
// Calculate new dimensions
$ratio = min($max_width / $orig_width, $max_height / $orig_height);
$new_width = intval($orig_width * $ratio);
$new_height = intval($orig_height * $ratio);
// Create image from source based on type
switch ($type) {
case IMAGETYPE_JPEG:
$image = imagecreatefromjpeg($source);
break;
case IMAGETYPE_PNG:
$image = imagecreatefrompng($source);
break;
case IMAGETYPE_GIF:
$image = imagecreatefromgif($source);
break;
default:
return false;
}
// Create new image
$new_image = imagecreatetruecolor($new_width, $new_height);
// Preserve transparency for PNG/GIF
if ($type === IMAGETYPE_PNG || $type === IMAGETYPE_GIF) {
imagealphablending($new_image, false);
imagesavealpha($new_image, true);
$transparent = imagecolorallocatealpha($new_image, 0, 0, 0, 127);
imagefill($new_image, 0, 0, $transparent);
}
// Resize
imagecopyresampled($new_image, $image, 0, 0, 0, 0,
$new_width, $new_height, $orig_width, $orig_height);
// Save resized image
switch ($type) {
case IMAGETYPE_JPEG:
imagejpeg($new_image, $destination, 90);
break;
case IMAGETYPE_PNG:
imagepng($new_image, $destination, 9);
break;
case IMAGETYPE_GIF:
imagegif($new_image, $destination);
break;
}
// Free memory
imagedestroy($image);
imagedestroy($new_image);
return true;
}
// Usage after upload
if (move_uploaded_file($_FILES['photo']['tmp_name'], $destination)) {
// Create thumbnail
$thumbnail = 'uploads/thumb_' . $new_filename;
resizeImage($destination, $thumbnail, 300, 300);
echo "Image uploaded and thumbnail created";
}
?>
Security Best Practices
File Upload Security Checklist:
- Always validate file extension AND MIME type
- Use
getimagesize()to verify real images - Set maximum file size limits
- Generate unique filenames (use
uniqid()) - Store uploads outside the web root when possible
- Never execute uploaded files
- Set proper file permissions (0644 for files)
- Validate file content, not just extension
- Use
move_uploaded_file()(it validates the file) - Scan files with antivirus if possible
Common Vulnerabilities:
- Trusting user-provided filename (rename files!)
- Not validating MIME type (can be spoofed)
- Allowing executable file types (.php, .exe)
- Storing uploads in web-accessible directories without protection
- Not limiting file size (DoS attack)
Practice Exercise
Task: Create a profile picture upload system with:
- Upload form accepting only images (JPG, PNG, GIF)
- Validate file type, MIME type, and size (max 2MB)
- Generate unique filename with timestamp
- Create two versions: original and thumbnail (150x150)
- Display both versions after upload
- Store files in "uploads/" directory
- Show file size in human-readable format
Bonus: Add ability to delete previous uploads and implement drag-and-drop upload interface.
Summary
In this lesson, you learned:
- The
$_FILESsuperglobal structure - Creating upload forms with proper
enctype - Basic file upload handling with
move_uploaded_file() - Upload error codes and handling
- Secure file validation (extension, MIME type, size, content)
- Generating unique filenames
- Handling multiple file uploads
- Helper functions for file operations
- Image resizing and thumbnail creation
- Critical security best practices
Next, we'll explore sessions and cookies in PHP!