homepage >> Image compression with PHP and Bash -    
 

Yossef Benharosh is an apt web developer and the author of the eBook The essentials of object oriented PHP.

Yossef Benharosh web developer profile linkedin twitter github

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:

Linux terminal cheat sheet

Upload files to Laravel app

Using jQuery and AJAX to dynamically update a page