One of the most common actions on the modern web application is upload, crop and resize of images, whether for avatar use or general use of resized images in galleries or as thumbnails.
This tutorial will cover all the needed techniques starting with an old school upload form and gradually building up a modern solution which you can then use in every day situations.
Table of Contents
Upload image with PHP
We will start this tutorial with a classic image upload so you can easily understand the means and methods of how uploading files in PHP works.
In this first example we will a form for uploading files and save that file in a folder on the server, but only if that file is an image.
To upload a file, you need a form with a special input field type of file. So, open your favorite editor and paste in this code and save it as form.php:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Upload Files using PHP</title>
</head>
<body>
<form action="upload.php" method="post" enctype="multipart/form-data">
<div class="row">
<label for="fileToUpload">Select a file to upload:</label><br>
<input type="file" name="fileToUpload" id="fileToUpload">
</div>
<div class="row">
<input type="submit" value="Upload">
</div>
</form>
</body>
</html>
One thing that you already noticed is probably the enctype attribute of our form. It basically prepares the form for binary data, like the contents of a file.
Next step is to create a file that will handle our uploaded images. Go ahead, create upload.php file and paste this in:
<?php
// fileToUpload is the name of our file input field
if (!isset($_FILES['fileToUpload'])) {
echo "No file uploaded.";
exit;
}
$file = $_FILES['fileToUpload'];
if ($file['error'] > 0) {
echo "Error: " . $file['error'] . "<br>";
} else {
echo "File name: " . htmlspecialchars($file['name'], ENT_QUOTES, 'UTF-8') . "<br>";
echo "File type: " . htmlspecialchars($file['type'], ENT_QUOTES, 'UTF-8') . "<br>";
echo "File size: " . round($file['size'] / 1024, 2) . " KB<br>";
echo "Temp path: " . htmlspecialchars($file['tmp_name'], ENT_QUOTES, 'UTF-8');
}
Now, open form.php in browser and try to select and upload a file. If everything went fine, you should see some info about the uploaded file when you submit a form.
Nice start, but user can now upload anything to your server and basically blow it to pieces. So, let’s modify the upload.php code to check if uploaded file is actually an image:
<?php
if (!isset($_FILES['fileToUpload'])) {
echo "No file uploaded.";
exit;
}
$file = $_FILES['fileToUpload'];
if ($file['error'] > 0) {
echo "Error: " . $file['error'] . "<br>";
} else {
// allowed extensions (lowercase, no leading dot for easier matching)
$validExtensions = ['jpg', 'jpeg', 'gif', 'png'];
// get extension safely
$ext = strtolower(pathinfo($file['name'], PATHINFO_EXTENSION));
// check if extension is valid
if (in_array($ext, $validExtensions, true)) {
echo "Uploaded file is allowed!";
} else {
echo "You must upload an image...";
}
}
So, we checked if the file is an image and now we can write code to move that image in some folder on the server, so change your code to this:
<?php
// Simple, safer image upload handler
if (!isset($_FILES['fileToUpload'])) {
exit('No file uploaded.');
}
$file = $_FILES['fileToUpload'];
if ($file['error'] !== UPLOAD_ERR_OK) {
exit('Error: ' . $file['error']);
}
// ---- Config ----
$targetDir = __DIR__ . '/uploads';
$allowedExt = ['jpg','jpeg','png','gif'];
$allowedMime = ['image/jpeg','image/png','image/gif'];
// Ensure uploads directory exists
if (!is_dir($targetDir)) {
if (!mkdir($targetDir, 0755, true) && !is_dir($targetDir)) {
exit('Failed to create uploads directory.');
}
}
// Validate extension
$origName = $file['name'];
$ext = strtolower(pathinfo($origName, PATHINFO_EXTENSION));
if (!in_array($ext, $allowedExt, true)) {
exit('You must upload an image...');
}
// Validate MIME (defense-in-depth)
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']);
if (!in_array($mime, $allowedMime, true)) {
exit('Invalid image type.');
}
// Sanitize filename and prepend timestamp
$safeName = preg_replace('/[^A-Za-z0-9._-]/', '_', basename($origName));
$newName = time() . '_' . $safeName;
$dest = $targetDir . '/' . $newName;
// Move file
if (!move_uploaded_file($file['tmp_name'], $dest)) {
exit('Failed to move uploaded file.');
}
echo 'File ' . htmlspecialchars($newName, ENT_QUOTES, 'UTF-8') . ' successfully copied';
Before you try this out, create a folder named uploads in the directory in which the code is in.
Congratulations, you successfully uploaded and saved a file on the server.
Crop and resize images with PHP
Now the fun part begins. We will resize the uploaded image and save resized version in uploads folder so you can easily use them later. We can do this from scratch, but there are plenty of useful libraries on the web that does an excellent job.
I will use a great ImageManipulator class. Download it and put inside your working directory.
Now, update your upload.php file:
<?php
// upload-resize.php
require_once __DIR__ . '/ImageManipulator.php';
if (!isset($_FILES['fileToUpload'])) {
exit('No file uploaded.');
}
$file = $_FILES['fileToUpload'];
if ($file['error'] !== UPLOAD_ERR_OK) {
exit('Error: ' . $file['error']);
}
// ---- Config ----
$targetDir = __DIR__ . '/uploads';
$allowedExt = ['jpg','jpeg','png','gif'];
$allowedMime = ['image/jpeg','image/png','image/gif'];
$targetWidth = 200;
$targetHeight = 200;
// Ensure upload dir exists
if (!is_dir($targetDir) && !mkdir($targetDir, 0755, true)) {
exit('Failed to create uploads directory.');
}
// Validate extension
$origName = $file['name'];
$ext = strtolower(pathinfo($origName, PATHINFO_EXTENSION));
if (!in_array($ext, $allowedExt, true)) {
exit('You must upload an image...');
}
// Validate MIME (defense in depth)
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']) ?: '';
if (!in_array($mime, $allowedMime, true)) {
exit('Invalid image type.');
}
// Process image
try {
$manipulator = new ImageManipulator($file['tmp_name']);
// Resize to 200x200 (stretches to exact size; if you want
// to keep aspect ratio, use crop logic instead)
$manipulator->resample($targetWidth, $targetHeight);
// Sanitize filename and prepend timestamp
$safeName = preg_replace('/[^A-Za-z0-9._-]/', '_', basename($origName));
$newName = time() . '_' . $safeName;
$manipulator->save($targetDir . '/' . $newName);
echo 'Done: ' . htmlspecialchars($newName, ENT_QUOTES, 'UTF-8');
} catch (Throwable $e) {
// Avoid leaking internals in prod; log if needed
exit('Image processing failed.');
}
The same class can be used to crop the image, so let’s change the code so the image cropped to 200×130 px. We want to crop the center of the image, so the good parts are intact, so we have to calculate crop coordinates:
<?php
// include ImageManipulator class
require_once __DIR__ . '/ImageManipulator.php';
if (!isset($_FILES['fileToUpload'])) {
exit('No file uploaded.');
}
$file = $_FILES['fileToUpload'];
if ($file['error'] !== UPLOAD_ERR_OK) {
exit('Error: ' . $file['error']);
}
// Config
$targetDir = __DIR__ . '/uploads';
$allowedExt = ['jpg','jpeg','gif','png'];
$allowedMime = ['image/jpeg','image/png','image/gif'];
// Ensure uploads dir exists
if (!is_dir($targetDir) && !mkdir($targetDir, 0755, true)) {
exit('Failed to create uploads directory.');
}
// Validate extension
$origName = $file['name'];
$ext = strtolower(pathinfo($origName, PATHINFO_EXTENSION));
if (!in_array($ext, $allowedExt, true)) {
exit('You must upload an image...');
}
// Validate MIME (defense-in-depth)
$finfo = new finfo(FILEINFO_MIME_TYPE);
$mime = $finfo->file($file['tmp_name']) ?: '';
if (!in_array($mime, $allowedMime, true)) {
exit('Invalid image type.');
}
// Process: center-crop to 200x130
try {
$manipulator = new ImageManipulator($file['tmp_name']);
$width = $manipulator->getWidth();
$height = $manipulator->getHeight();
$centreX = (int) round($width / 2);
$centreY = (int) round($height / 2);
// Target dimensions
$cropW = 200;
$cropH = 130;
$x1 = max(0, $centreX - (int)($cropW / 2));
$y1 = max(0, $centreY - (int)($cropH / 2));
$x2 = min($width, $x1 + $cropW);
$y2 = min($height, $y1 + $cropH);
// Adjust if near edges to guarantee exact size
if (($x2 - $x1) < $cropW) { $x1 = max(0, $x2 - $cropW); }
if (($y2 - $y1) < $cropH) { $y1 = max(0, $y2 - $cropH); }
$manipulator->crop($x1, $y1, $x2, $y2);
// Sanitize filename and save
$safeName = preg_replace('/[^A-Za-z0-9._-]/', '_', basename($origName));
$newName = time() . '_' . $safeName;
$manipulator->save($targetDir . '/' . $newName);
echo 'Done ... ' . htmlspecialchars($newName, ENT_QUOTES, 'UTF-8');
} catch (Throwable $e) {
exit('Image processing failed.');
}
Awesome. Now you learned how to upload, resize and crop images using pure PHP. It is time to make some modern variations on the theme.
Join us next week to see how to upload, crop and resize images in a more modern way using jQuery and HTML5.
I see you’re checking that the file is an image by verifying the extension. I was recently doing something similar and found out that checking the MIME type is a more accurate way of checking files even those that have fake extensions. I’m also still wondering if MIME types are a robust means of doing so. Thanks for your tutorials though, they’re quite enjoyable.
Hi,
this is just basic check using extension. For more robust (and secure) checking, read http://php.robm.me.uk/#toc-FileUploads
Hi,
Just a quick q for you. Why do we have to do this:
$newImage = $manipulator->resample(200, 200);
are we using $newImage anywhere later on?
Thanks
S
Works great! Thank you.
Hi, your tutorial is excellent and the best I have come across so for, but I realize it will allow just one image at a time. How can I get it to upload multiple images for a unique real estate property. I would appreciate your assistance very much. Thank you.
Comments are closed.