When users upload images from various devices, the orientation metadata (EXIF data) may cause the images to appear rotated incorrectly. This blog post will guide you through fixing image rotation using client-side JavaScript to capture user input and server-side PHP with the GD library for processing. We'll provide code snippets and explanations for each step.
Step1: Client-Side (HTML + JavaScript)
HTML Form
The HTML form allows users to upload an image and provides buttons to rotate the image by 90 degrees clockwise and counterclockwise.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Image Upload with Rotation</title>
<style>
#preview {
display: block;
margin-top: 10px;
max-width: 200px; /* Adjust thumbnail size */
}
</style>
</head>
<body>
<form id="uploadForm" enctype="multipart/form-data">
<input type="file" name="image" id="image" accept="image/png, image/jpeg" onchange="showThumbnail(this)">
<button type="button" onclick="rotateImage(90)">Rotate 90° CW</button>
<input type="hidden" name="rotation" id="rotation" value="0">
<button type="submit">Upload</button>
</form>
<img id="preview" src="" alt="Image preview">
</body>
</html>
JavaScript
The JavaScript handles image rotation and captures the rotation angle to send it along with the image when the form is submitted.
let rotation = 0;
function showThumbnail(input) {
const file = input.files[0];
const reader = new FileReader();
reader.onload = function(e) {
const img = document.getElementById('preview');
img.src = e.target.result;
img.style.transform = 'rotate(0deg)'; // Reset rotation
};
reader.readAsDataURL(file);
}
function rotateImage(angle) {
rotation = (rotation + angle) % 360;
document.getElementById('rotation').value = rotation;
const img = document.getElementById('preview');
img.style.transform = rotate(${rotation}deg);
}
document.getElementById('uploadForm').addEventListener('submit', function(e) {
e.preventDefault();
const formData = new FormData(this);
fetch('upload.php', {
method: 'POST',
body: formData
})
.then(response => response.text())
.then(data => alert(data))
.catch(error => console.error('Error:', error));
});
Explanation
- HTML Form:
- <input type="file" name="image" id="image" accept="image/png, image/jpeg" onchange="showThumbnail(this)">: Allows the user to select an image file and triggers the
showThumbnail
function to display a preview of the image. - <button type="button" onclick="rotateImage(90)">Rotate 90° CW</button>: Button to rotate the image 90 degrees clockwise. The
rotateImage
function updates the rotation angle and the image preview. - <input type="hidden" name="rotation" id="rotation" value="0">: Hidden input to store the rotation angle, which is sent to the server when the form is submitted.
- <input type="file" name="image" id="image" accept="image/png, image/jpeg" onchange="showThumbnail(this)">: Allows the user to select an image file and triggers the
- JavaScript:
- showThumbnail(input): This function reads the selected image file and displays a thumbnail preview. It also resets the rotation when a new image is selected.
- rotateImage(angle): This function updates the rotation angle when the user clicks one of the rotation buttons. The angle is stored in the hidden input field named
rotation
and applied to the image preview. - Event Listener: The form submission is intercepted to handle the file upload using JavaScript's Fetch API. The
rotation
value and the image file are sent to the server.
Step2: Server-Side Code (PHP with GD)
In Step 1, we covered the client-side HTML and JavaScript code to capture user input for image rotation and display a thumbnail preview. In this step, we will cover the server-side PHP script using the GD library to process the uploaded image, apply the specified rotation, and compress the image before saving it to the server. Additionally, we'll provide information on how to handle different image formats like PNG, JPG, and JPEG.
The PHP script processes the uploaded image, applies the specified rotation, and compresses the image before saving it to the server.
<?php
function rotateImage($sourcePath, $destinationPath, $angle, $mime) {
switch ($mime) {
case 'image/jpeg':
case 'image/jpg':
$sourceImage = imagecreatefromjpeg($sourcePath);
break;
case 'image/png':
$sourceImage = imagecreatefrompng($sourcePath);
break;
default:
return false;
}
$rotatedImage = imagerotate($sourceImage, -$angle, 0); // Negative angle for correct rotation direction
switch ($mime) {
case 'image/jpeg':
case 'image/jpg':
imagejpeg($rotatedImage, $destinationPath, 75); // Adjust the quality as needed (e.g., 75)
break;
case 'image/png':
imagepng($rotatedImage, $destinationPath);
break;
}
imagedestroy($sourceImage);
imagedestroy($rotatedImage);
return true;
}
// Define directory paths
$uploadDirectory = 'upload/';
$thumbnailDirectory = 'thumbnail/';
// Ensure directories exist (create if necessary)
if (!file_exists($uploadDirectory)) {
mkdir($uploadDirectory, 0755, true);
}
if (!file_exists($thumbnailDirectory)) {
mkdir($thumbnailDirectory, 0755, true);
}
// Process uploaded file
if (isset($_FILES['image'])) {
$sourcePath = $_FILES['image']['tmp_name'];
$originalFilename = $_FILES['image']['name'];
$rotation = intval($_POST['rotation']);
$mime = mime_content_type($sourcePath);
// Define paths for the processed images
$uniqueFilename = uniqid(time(), true) . '.jpg'; // Use .jpg extension
$lightCompressedPath = $uploadDirectory . $uniqueFilename;
$hardCompressedPath = $thumbnailDirectory . $uniqueFilename;
// Step 1: Rotate the image based on user input
if (rotateImage($sourcePath, $lightCompressedPath, $rotation, $mime)) {
// Step 2: Create a hard compressed version of the image
if (!compressImage($lightCompressedPath, $hardCompressedPath, $mime, 50)) {
echo "Failed to process hard compression.<br>";
} else {
echo "Image uploaded and processed successfully!<br>";
}
} else {
echo "Failed to rotate image.<br>";
}
} else {
echo "No image uploaded.";
}
function compressImage($sourcePath, $destinationPath, $mime, $quality) {
switch ($mime) {
case 'image/jpeg':
case 'image/jpg':
$sourceImage = imagecreatefromjpeg($sourcePath);
imagejpeg($sourceImage, $destinationPath, $quality);
break;
case 'image/png':
$sourceImage = imagecreatefrompng($sourcePath);
imagepng($sourceImage, $destinationPath);
break;
default:
return false;
}
imagedestroy($sourceImage);
return true;
}
?>
Explanation
- rotateImage($sourcePath, $destinationPath, $angle, $mime): This function uses the GD library to rotate the image by the specified angle based on its MIME type (JPEG or PNG) and save it with a quality of 75 for JPEGs.
- compressImage($sourcePath, $destinationPath, $mime, $quality): This function uses the GD library to compress the image to the specified quality level, handling both JPEG and PNG formats.
- Directory Paths: Define paths for the upload and thumbnail directories and ensure they exist and are writable.
- Process Uploaded File: Handle the uploaded file, apply the user-specified rotation, and compress the image before saving it.
Conclusion
By combining client-side JavaScript to capture user input and server-side PHP with the GD library to process images, you can ensure that uploaded images are correctly oriented and compressed. This approach provides a better user experience and ensures consistent image handling across different devices.
Feel free to adapt and expand on this code to suit your specific needs. Happy coding!
For those looking for a solution using the Imagick library, check out our blog post on how to fix image rotation using JavaScript and PHP with Imagick.
Leave a Reply