Image compression with PHP and Bash - quick and dirty
A client of mine wanted his website to have the ability to compress images that users upload. Even though I recommended the use of an established cloud service he insisted on doing everything in-house. Since I'm a fan of working with Bash and it is a PHP website I decided to use Bash utilities for image compression and came up with a quick and dirty solution that seems to satisfy the need.
The Bash utilities
Install the following utilities to handle JPG and PNG compression:
$ sudo apt-get update
$ sudo apt-get install jpegoptim
$ sudo apt-get install optipng
The HTML
The form enables the upload of an image with ajax:
<img src="/placeholder.jpg" id="theImage" />
<button id="theTrigger">Upload file</button>
<input type="file" id="file" style="display:none"/>
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
The JavaScript
A jQuery script to read and validate the image then send to the backend:
(function($){
// Limit the allowed file size
var imgSizeLimit = 20*1024*1024;
// The FileList object represents the files uploaded with the file upload button.
// It contains the data for the name, size, extension and content of the files
function uploadImage() {
// Get the file input field
var img = $("#file");
// Get the uploaded file
var file = img[0].files[0];
// Validations
if(file.size > imgSizeLimit){
alert("The image is too large");
return;
}
if(file.type !== 'image/png' && file.type !== 'image/jpg' && file.type !== 'image/jpeg'){
alert("Only jpeg or png extensions are allowed");
return;
}
// The FileReader object lets apps read asynchronously the contents of files
var r = new FileReader();
// Fire after the reading of the file ended
r.addEventListener("load", function(e) {
// Send the data with AJAX to the server and get the response
$.post('/create_new_img.php', {'f': e.target.result}, function(res){
res = JSON.parse(res);
if(res.success === true){
$("#theImage").attr('src', res.path);
} else {
alert("General error");
console.log(res.reason);
}
});
});
// Read the content of the file
r.readAsDataURL(file);
}
// Listens to any change to the #file element
// then fires the uploadImage function
// that receives the files property
$("#file").on("change", function(){
uploadImage();
});
// Clicking #theTrigger button triggers clicking
// the #file element
$("#theTrigger").on("click", function(){
$("#file").trigger("click");
});
}(jQuery));
The PHP
A PHP script to validate, upload and compress via Bash utilities:
<?php
if (isset($_POST['f'])) {
// Upload folder
$destinationFolder = USER_UPLOAD_DIR;
// Maximum allowed size in Mb
$maxSize = 20;
$maxFileSize = $maxSize*1024*1024; // bytes
// GET the posted data
$file = $_POST['f'];
// Use getimagesize to find the file extension
// Only png / jpg mime types are allowed
$size = getimagesize ($file);
$extension = $ext = $size['mime'];
if($ext == 'image/jpeg')
$ext = '.jpg';
elseif($ext == 'image/png')
$ext = '.png';
else
exit(json_encode(['success'=>false, 'reason'=>'only png and jpg types are allowed']));
// Prevent the upload of large files
if(strlen(base64_decode($file)) > $maxFileSize)
exit(json_encode(['success'=>false, 'reason'=>"file size exceeds {$maxFileSize} Mb"]));
// Remove inline tags and spaces
$img = str_replace('data:image/png;base64,', '', $file);
$img = str_replace('data:image/jpeg;base64,', '', $img);
$img = str_replace(' ', '+', $img);
// Read base64 encoded string as an image
$img = base64_decode($img);
// Give the image a unique name. Don't forget the extension
$filename = date("d_m_Y_H_i_s")."-".time().$ext;
// The path to the newly created file inside the upload folder
$destinationPath = "$destinationFolder$filename";
// Create the file or return false
$success = file_put_contents($destinationPath, $img);
// You can make a copy. Here I compress the source
$compressedImg = $destinationPath;
// Run the compression commands with the shell_exec function
// Here I use maximum compression. Read the utilities documentation
// to find the best options for your project
$max_quality = 75;
if ($extension == 'image/jpeg') :
$command = "jpegoptim --max=$max_quality --strip-all --all-progressive";
$output = shell_exec("$command $compressedImg 2>&1");
elseif ($extension == 'image/png') :
$command = 'optipng -o7 -preserve -strip all';
$output = shell_exec("$command $compressedImg 2>&1");
endif;
// Secure the iamge
chmod($compressedImg, 0644);
exit(json_encode(['success'=>true,'path'=>$compressedImg]));
}
Recommended for you:
Using jQuery and AJAX to dynamically update a page