Learn how to save disk space (and waste CPU time) by generating image thumbnails on the fly with PHP and GD libraries.

Save disk or CPU?

The answer is easy, since your ISP normally charge for disk space and not for CPU usage, nowaday it’s the disk space shortage that more freqently happens.

The approach described here, permits you to generate “on the fly” all the scaled images you need for your site, just using standard PHP functions as provided from the GD library.

Before you start

You should check that your php_info(); function output has a section concerning GD: it should look like this:

gd

GD Support enabled
GD Version 2.0 or higher
FreeType Support enabled
FreeType Linkage with freetype
FreeType Version 2.1.9
GIF Read Support enabled
GIF Create Support enabled
JPG Support enabled
PNG Support enabled
WBMP Support enabled

If you see something similar then you’re ok, else you should check for your PHP installation or change ISP.

This script was tested under Linux, I don’t know if it works under other OS’s (but why should’nt?)

The idea

The idea is quite simple, and is not particularly original (I took some code from other projects), I prepared a flexible script that can both accept parameters via GET or POST requests or read values from already defined variables.

The script can hence works standalone or included from other scripts.

Imagine you have an image that you need to resize on the fly, the image is stored sa images/image1.jpg under your document root, imagine also that the PHP script is called getthumb.php and is stored directly under the document root, to get a resized image 100 px wide you just call this url: http://www.yoursite.com/getthumb.php?width=100&img=image1.jpg

you usually want to mantain height/width ratio without distorcing the image, this is why you can specify only one dimension (x or y) and the script will do all the calculations for you.

Of course you can also save the thumbnail to disk, but this is normally accomplished through a calling script, just define the following vars in the calling script:

 * @param   string  $img_path image file path, relative to this script, if not
 *                            set, the next is checked on GET
 * @param   string  $img urlencoded image file path, relative to this script
 *                      (in vncms should be 2 level depth)
 * @param   integer $width max width
 * @param   integer $height max height
 * @param   string  $jpgfilename optional filename to store image in, if
 *                            not given, image is outputted to the browser

Finally, the code!


/**
 * @copyright 2005 Alessandro Pasotti 
 * @license GPL
 * @author  Alessandro Pasotti (some source grabbed somewhere)
 *
 * Read an image file and send a scaled thumbnail to the browser
 * default to 150 px width.
 *
 * This file can also be included, in this case, define vars in the calling script
 *
 * @param   string  $img_path image file path, relative to this script, if not
 *                            set, the next is checked on GET
 * @param   string  $img urlencoded image file path, relative to this script
 *                      (in vncms should be 2 level depth)
 * @param   integer $width max width
 * @param   integer $height max height
 * @param   string  $jpgfilename optional filename to store image in, if
 *                            not given, image is outputted to the browser
 *
 *
 * Example calls:
 *
 * getthumb.php?img=../../images/logo.jpg              --> default to max 150 (w or h)
 * getthumb.php?img=../../images/logo.jpg&height=200   --> resize to 200px height
 *
 *
 */


// Constants
define('DEF_WIDTH',     150);  // default
define('DEF_HEIGHT',    150);  // default
define('ALLOWED_WIDTH', 1024); // no tampering: set up a max
define('ALLOWED_HEIGHT',768);  // no tampering: set up a max
define('DEF_QUALITY',   90);   // default quality

define('DEBUG', false);


// I put this here to avoid any external dependency
if(!function_exists('__clean')) {
  function __clean($s, $len){
      if(!isset($s)) return null;
      $s = substr($s, 0, $len);
      if(ini_get('gpc_magic_quotes') != 1) $s = addslashes($s);
      $s = escapeshellcmd($s);
      return $s;
  }
}

// Get image location if not defined elsewhere
if(!isset($image_path))  $image_path = urldecode(stripslashes(__clean($_GET['img'], 256)));
if(!isset($width))       $width      = __clean($_GET['width'], 4);
if(!isset($height))      $height     = __clean($_GET['height'], 4);

// if both are not defined:
if(!$width && !$height) {
  $width = DEF_WIDTH;
  $height= DEF_HEIGHT;
}

if($width) $width   =  min(ALLOWED_WIDTH, $width);
else $width = ALLOWED_WIDTH;
if($height) $height =  min(ALLOWED_HEIGHT, $height);
else $height = ALLOWED_HEIGHT;


define('MAX_HEIGHT', $height);
define('MAX_WIDTH' , $width);


// Load image
$img = null;
$imginfo = getimagesize($image_path);
$imgtype = $imginfo[2];

$ext = strtolower(end(explode('.', $image_path)));
if ($imgtype == IMAGETYPE_JPEG) {
    $img = @imagecreatefromjpeg($image_path);
} else if ($imgtype == IMAGETYPE_PNG) {
    $img = @imagecreatefrompng($image_path);
    //echo "IMGEBASE " . IMAGE_BASE . ' path: ' .  $image_path; exit;
// Only if your version of GD includes GIF support
} else if ($imgtype == IMAGETYPE_GIF) {
    $img = @imagecreatefromgif($image_path);
}

// If an image was successfully loaded, test the image for size
if ($img) {

    // Get image size and scale ratio
    $width = imagesx($img);
    $height = imagesy($img);
    $scale = min(MAX_WIDTH/$width, MAX_HEIGHT/$height);
    if(defined('DEBUG') && DEBUG) {
      user_error("thumbnails.php - IMAGE: $image_path  WIDTH: $width HEIGHT: $height SCALE: $scale<hr />");
      exit;
    }

    // If the image is larger than max shrink it
    if ($scale < 1) {
        $new_width = floor($scale*$width);
        $new_height = floor($scale*$height);

        // Create a new temporary image
        $tmp_img = imagecreatetruecolor($new_width, $new_height);

        // Copy and resize old image into new image
        imagecopyresized($tmp_img, $img, 0, 0, 0, 0,
                         $new_width, $new_height, $width, $height);
        imagedestroy($img);
        $img = $tmp_img;
    }
}

// Create error image if necessary
if (!$img) {
    $img = imagecreate(DEF_WIDTH, DEF_HEIGHT);
    imagecolorallocate($img,255,255,255);
    $c2 = imagecolorallocate($img,255,0,0);
    imageline($img,0,0,DEF_WIDTH,DEF_HEIGHT,$c2);
    imageline($img,DEF_WIDTH,0,0,DEF_HEIGHT,$c2);
}

if(!$jpgfilename) {
  // Display the image
  header("Content-type: image/jpg");
  imagejpeg($img, null, DEF_QUALITY);
  exit;
} else {
   if(defined('DEBUG') && DEBUG) {
      echo "thumbnails.php - saving to $jpgfilename<hr>";
  }
  imagejpeg($img, $jpgfilename, DEF_QUALITY);
}

I hope you find this small script useful, it’s really very flexible and you can use it freely as you like.

Happy resizing!

5 Responses to “On the fly thumbnails generation with PHP”

  • DGM

    Many shared hosting problems have CPU sharing problems, but lots of disk space, the opposite of what you describe…

  • Dirk

    You forgot caching. Generating thumbs always new causes high CPU load. Ah, forgot, CPU is unlimited 🙂
    But in case CPU power is limited:
    build something like md5($name . $date . $widthNew . $heightNew) . “.jpg”, write it as file and before generating a new thumb check if this filename is present already.

  • Alessandro Pasotti

    Thanks to both commenters!

    @dgm: the world is large, in my (very limited) experience disk space is the bottleneck, but of course it may vary depending on the number of page views and many other factors.

    In case CPU is limited (or is the bottleneck), I would follow Dirk’s advice (and I really did it in a few projects).

  • Simon Poulston

    Hi,

    This code is good, however, I found that I got a much better image by using:

    imagecopyresampled

    instead of

    imagecopyresized

    I think that this may be due to the fact that I am using s GD version above 2.

    Thanks