How to Crop Image in PHP and JavaScript before Uploading
Image Crop and Upload is an essential feature of web applications for allows users to crop the image to aspected size before uploading it. This feature is generally implemented in the user’s profile section to manage user profile pictures to upload user profile pictures after image crop to display perfectly on the user profile page. However, this functionality could be implemented anywhere with an image (with extension .jpg, .png, .webp) uploaded in web applications.
To achieve this functionality we will follow these simple steps:
- Create HTML structure
- Create a javascript function for cropping image
- Create database connection
- Write php logic to move the image into a folder and save it in the database
Create HTML structure
create index.php and paste this HTML code it includes form structure and bootstrap 5 modal added crop button and do not crop button
<!DOCTYPE html>
<html>
<head>
<title>Php Crop Image Before Upload Example - abdultechhub.com</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.3/css/bootstrap.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.6/cropper.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap/5.2.3/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/cropperjs/1.5.6/cropper.js"></script>
</head>
<style type="text/css">
body {
background: #f7fbf8;
}
h1 {
font-weight: bold;
font-size: 23px;
}
img {
display: block;
max-width: 100%;
}
.preview {
text-align: center;
overflow: hidden;
width: 160px;
height: 160px;
margin: 10px;
border: 1px solid red;
}
input {
margin-top: 40px;
}
.section {
margin-top: 150px;
background: #fff;
padding: 50px 30px;
}
.modal-lg {
max-width: 1000px !important;
}
.error {
color: red;
}
</style>
<body>
<div class="container">
<div class="row">
<div class="col-md-8 offset-md-2 section text-center">
<?php
if (isset($_GET['success']) && $_GET['success'] == 1) {
?>
<div class="alert alert-success alert-dismissible fade show text-left" role="alert">
<strong>Success!</strong> File Uploaded successfully
<a href="index.php" class="btn-close"></a>
</div>
<?php
}
?>
<h1>PHP Crop Image Before Upload Example - abdultechhub.com</h1>
<form action="upload_image.php" method="POST">
<input type="file" name="image" class="image form-control" accept=".jpg, .jpeg, .png, .webp">
<span class="error" id="fileError"></span>
<input type="hidden" name="image_base64" id="imageBase64">
<span class="error" id="base64Error"></span>
<img src="" style="width: 200px;display: none;" class="show-image">
<br />
<button class="btn btn-success">Submit</button>
</form>
</div>
</div>
</div>
<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="modalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="exampleModalLabel">New message</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="img-container">
<div class="row">
<div class="col-md-8">
<img id="image" src="https://avatars0.githubusercontent.com/u/3456749">
</div>
<div class="col-md-4">
<div class="preview"></div>
</div>
</div>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" id="doNotCrop">Do not crop</button>
<button type="button" class="btn btn-primary" id="crop">Crop</button>
</div>
</div>
</div>
</div>
</body>
</html>
Aspect Ratio:
if you want to crop images in aspect ratio like 1:1 or 1:2 or 2:3 etc then you can define it in aspectRatio parameters like below. I have added aspectRatio: 1 which means the image will crop in perfect square (1:1)
$modal.on('shown.bs.modal', function() {
cropper = new Cropper(image, {
aspectRatio: 1, // ration 1:1
//aspectRatio: 2/3, // ration 2:3
viewMode: 3,
preview: '.preview'
});
}).on('hidden.bs.modal', function() {
cropper.destroy();
cropper = null;
});
Create database connection
Now we have to create a database connection here I am creating a DB connection using PDO which is more secure you can also create it using mysli.
<?php
// Database connection details
$dbHost = 'localhost';
$dbName = 'image_crop';
$dbUser = 'root';
$dbPassword = '';
try {
// Create a PDO connection
$pdo = new PDO("mysql:host=$dbHost;dbname=$dbName", $dbUser, $dbPassword);
// Set the PDO error mode to exception
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
die('Error: ' . $e->getMessage());
}
Write php logic to move the image into a folder and save it in the database
<?php
include 'connection.php';
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// Check if the image is provided
if (empty($_POST['image_base64'])) {
die('Error: Base64 image data is required.');
}
// Get the base64 encoded image data
$imageData = $_POST['image_base64'];
// Validate if the image is a PNG or JPG
if (!preg_match('/^data:image\/(png|jpeg);base64,/', $imageData, $imageType)) {
die('Error: Only PNG or JPG images are allowed.');
}
// Extract the image type (png or jpeg)
$imageType = $imageType[1];
// Convert base64 data to binary
$binaryImageData = base64_decode(preg_replace('#^data:image/\w+;base64,#i', '', $imageData));
// Check if the file is empty after decoding
if (empty($binaryImageData)) {
die('Error: Invalid base64 image data.');
}
// Generate a unique filename
$imageName = time() . '.' . $imageType;
// Define the upload directory
$uploadDirectory = 'uploads/';
// Create the uploads directory if it doesn't exist
if (!is_dir($uploadDirectory)) {
mkdir($uploadDirectory, 0755, true);
}
// Define the file path
$filePath = $uploadDirectory . $imageName;
// Save the image to the server
file_put_contents($filePath, $binaryImageData);
// Insert image details into the database
try {
$stmt = $pdo->prepare("INSERT INTO crop_images (image_path, created_at) VALUES (:filename, NOW())");
$stmt->bindParam(':filename', $imageName);
$stmt->execute();
} catch (PDOException $e) {
die('Error inserting data into the database: ' . $e->getMessage());
}
// Redirect back to index.php with a success message
header('Location: index.php?success=1'); // Append success=1 as a query parameter
exit();
} else {
// If the form is not submitted, redirect to the homepage or display an error message
header('Location: index.php'); // Change 'index.php' to the actual filename or URL
exit();
}
Create a javascript function for cropping image
now we have to implement crop and do not crop functions and also create form validation function that allows only aspected image extension:
var $modal = $('#modal');
var image = document.getElementById('image');
var cropper;
$("body").on("change", ".image", function(e) {
var files = e.target.files;
var done = function(url) {
image.src = url;
$modal.modal('show');
};
var reader;
var file;
var url;
if (files && files.length > 0) {
file = files[0];
// Check if the file extension is allowed
var allowedExtensions = /(\.jpg|\.jpeg|\.png|\.webp)$/i;
if (!allowedExtensions.exec(file.name)) {
$('#fileError').text('Only JPG, JPEG, PNG, and WebP files are allowed.');
return;
} else {
$('#fileError').text('');
}
if (URL) {
done(URL.createObjectURL(file));
} else if (FileReader) {
reader = new FileReader();
reader.onload = function(e) {
done(reader.result);
};
reader.readAsDataURL(file);
}
}
});
$modal.on('shown.bs.modal', function() {
cropper = new Cropper(image, {
aspectRatio: 0,
viewMode: 3,
preview: '.preview'
});
}).on('hidden.bs.modal', function() {
cropper.destroy();
cropper = null;
});
$("#crop").click(function() {
canvas = cropper.getCroppedCanvas({
width: 160,
height: 160,
});
canvas.toBlob(function(blob) {
url = URL.createObjectURL(blob);
var reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = function() {
var base64data = reader.result;
$("#imageBase64").val(base64data);
$(".show-image").show();
$(".show-image").attr("src", base64data);
$("#modal").modal('toggle');
}
});
});
// Do Not Crop button click event
$("#doNotCrop").click(function() {
var input = $(".image")[0];
var file = input.files[0];
var reader = new FileReader();
reader.onloadend = function() {
var base64data = reader.result;
$("#imageBase64").val(base64data);
$(".show-image").show();
$(".show-image").attr("src", base64data);
$("#modal").modal('toggle');
};
reader.readAsDataURL(file);
});
// Validate base64 input on form submit
$("form").submit(function() {
if ($("#imageBase64").val() === "") {
$('#base64Error').text('Base64 input is required.');
return false;
} else {
$('#base64Error').text('');
}
});