How to cache images generated by PHP

When spitting out images with PHP, the browser may not cache the resulting image. So this is how you tell it to cache an image generated with PHP:

// put this above any php image generation code:
session_start();
header("Cache-Control: private, max-age=10800, pre-check=10800");
header("Pragma: private");
header("Expires: " . date(DATE_RFC822,strtotime(" 2 day")));

Option Number 1:
if the browser has a cached version of this page, always tell it to use it:

// the browser will send a $_SERVER['HTTP_IF_MODIFIED_SINCE'] if it has a cached copy 
if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])){
// if the browser has a cached version of this image, send 304
header('Last-Modified: '.$_SERVER['HTTP_IF_MODIFIED_SINCE'],true,304);
exit;
}

Option Number 2:
if the browser has a cached version of this page, and the file we’re generating an automatic thumbnail off hasn’t changed, we tell the browser to use the cached version.

$img = "some_image.png";
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])
&&
(strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == filemtime($img))) {
// send the last mod time of the file back
header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($img)).' GMT',
true, 304);
exit;
}

Full example with image thumbnail generation code:

session_start(); 
header("Cache-Control: private, max-age=10800, pre-check=10800");
header("Pragma: private");
header("Expires: " . date(DATE_RFC822,strtotime(" 2 day"))); $img = "some_image.png";
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && (strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) == filemtime($img))) { // send the last mod time of the file back header('Last-Modified: '.gmdate('D, d M Y H:i:s', filemtime($img)).' GMT', true, 304); exit; }
// generate a thumbnail off the $img file, and tell the browser to cache the result.
$is = new ImageSizer($img);
$is->resizeImage(75); // resize image to 75px wide
// and here we send the image to the browser with all the stuff required for tell it to cache
header("Content-type: image/jpeg");
header('Last-Modified: ' . gmdate('D, d M Y H:i:s', filemtime($thumbnail)) . ' GMT');
$is->showImage();

class ImageSizer { var $_baseDir; var $_baseImg; var $_imgData; var $_newImg; var $_newData; var $_newFormat; var $_loadPath; var $_defaultColor = array(255,255,255); var $keepAspectRatio; var $makeBigger; function ImageSizer($baseDir) { $this->changeBaseDir($baseDir); $this->keepAspectRatio = true; $this->makeBigger = false; } function changeBaseDir($baseDir) { $this->_baseDir = $baseDir; } function setDefaultColor($r, $g, $b) { $this->_defaultColor = array($r, $g, $b); } function changeFormat($str) { $str = strtolower($str); if ($str == 'jpg') $str = "jpeg"; $acceptable_formats = array('jpeg', 'gif', 'png'); if (!in_array($str, $acceptable_formats)) return false; $this->_newFormat = $str; } function loadImage($imgPath) { // $this->_imgData = getimagesize($this->_baseDir. $imgPath); $this->_imgData = getimagesize( $imgPath); $this->_imgData['funcType'] = preg_replace('#image/#i', '', $this->_imgData['mime']); $acceptable_formats = array('jpeg', 'gif', 'png'); if (!in_array($this->_imgData['funcType'], $acceptable_formats)) return false; $this->_newData = $this->_imgData; $funcName = 'imagecreatefrom' . $this->_imgData['funcType']; //$this->_newImg = $this->_baseImg = $funcName($this->_baseDir. $imgPath); $this->_newImg = $this->_baseImg = @$funcName( $imgPath); if(!$this->_baseImg){ echo "
Failed on this image $imgPath
"; return false; } $this->_loadPath = $imgPath; return true; } /*function genImageData(){ $funcName = 'image'.$this->getNewType(); $data = $funcName($this->_newImg ? $this->_newImg : $this->_baseImg); return $data; }*/ function resizeImage($w, $h, $crop=false) { $current_w = $this->getWidth(); $current_h = $this->getHeight(); $src_x = $src_y = 0; $dst_x = $dst_y = 0; if($w && $h && $crop=="f"){ // fill in the image centre it in white. // all the src stuff stays the same, just changing the destination suff $src_x = 0; $src_y = 0; $current_h = $current_h; $current_w = $current_w; // the destination stuff changes if($h && $w){ $h_percent = $percent = $h / $current_h; $w_percent = $percent = $w / $current_w; $percent = min($h_percent, $w_percent); }else if($h){ $h_percent = $percent = $h / $current_h; $percent = $h_percent; }else if($w){ $w_percent = $percent = $w / $current_w; $percent = $w_percent; } $dst_w = $current_w * $percent; $dst_h = $current_h * $percent; $new_w = $w; $new_h = $h; // work out destination x and y points $dst_x = ($new_w - $dst_w)/2; $dst_y = ($new_h - $dst_h)/2; }else if($w && $h && $crop=="c"){ $dst_w = $w; $dst_h = $h; $new_w = $w; $new_h = $h; // if the image we are tyring to crop is smaller than the crop request we dont do aynthing if($w > $current_w || $h > $current_h){ // the image is smaller than we are trying to crop! }else{ //the image is bigger on x and y axis. // check if we can fit horizontally $w_percent = $current_w/$w; $h_percent = $current_h/$h; if($w_percent < $h_percent){ $src_x = 0; $src_y = ($current_h/2) - (($h * $w_percent)/2); $current_w = $current_w; $current_h = ($h * $w_percent); }else{ $src_x = ($current_w/2) - (($w * $h_percent)/2); $src_y = 0; $current_w = ($w * $h_percent); $current_h = $current_h; } } }else if ($this->keepAspectRatio) { $percent = 1; // if ($current_w > $w || $current_h > $h || $this->makeBigger) { $do_resize=false; if($w && $current_w > $w){ $do_resize=true; }else if($h && $current_h > $h){ $do_resize=true; }else if($w && $current_w < $w && $this->makeBigger){ $do_resize=true; }else{ // imaeg is alreaedy smaller than requested size } if ( $do_resize ) { if($h && $w){ $h_percent = $percent = $h / $current_h; $w_percent = $percent = $w / $current_w; $percent = min($h_percent, $w_percent); }else if($h){ $h_percent = $percent = $h / $current_h; $percent = $h_percent; }else if($w){ $w_percent = $percent = $w / $current_w; $percent = $w_percent; } } $dst_w = $new_w = $current_w * $percent; $dst_h = $new_h = $current_h * $percent; } else{ $dst_w = $new_w = $w; $dst_h = $new_h = $h; } $this->_newImg = ImageCreateTrueColor($new_w, $new_h); $this->_newData = array($new_w, $new_h); if ($this->getNewType() == 'png' || $this->getNewType() == 'gif') { // This preserves the transparency imageAlphaBlending($this->_newImg, false); imageSaveAlpha($this->_newImg, true); } else { // This is if converting from PNG to another image format list($r, $g, $b) = $this->_defaultColor; $color = imagecolorallocate($this->_newImg, $r, $g, $b); imagefilledrectangle($this->_newImg, 0,0, $new_w, $new_h, $color); } // dst_w dst_h src_w src_h imagecopyresampled($this->_newImg, $this->_baseImg, $dst_x, $dst_y,$src_x,$src_y, $dst_w, $dst_h, $current_w, $current_h); return true; } function showImage() { header('Content-type: ' . $this->getNewMime()); $funcName = 'image'.$this->getNewType(); $funcName($this->_newImg ? $this->_newImg : $this->_baseImg); } function saveToFile($fileloc) { $funcName = 'image'.$this->getNewType(); $funcName($this->_newImg ? $this->_newImg : $this->_baseImg, $fileloc,100); } function addWatermark($pngloc,$offset_x=0) { $overlay = imagecreatefrompng($pngloc); imageAlphaBlending($overlay, false); imageSaveAlpha($overlay, true); imagecopy($this->_newImg, $overlay, (imagesx($this->_newImg))-(imagesx($overlay)+$offset_x), (imagesy($this->_newImg))-(imagesy($overlay)), 0, 0, imagesx($overlay), imagesy($overlay)); } function getBaseDir() { return $this->_baseDir; } function getWidth() { return $this->_imgData[0]; } function getHeight() { return $this->_imgData[1]; } function getMime() { return $this->_imgData['mime']; } function getType() { return $this->_imgData['funcType'];} function getNewWidth() { return $this->_newData[0]; } function getNewHeight() { return $this->_newData[1]; } function getNewMime() {return $this->_newFormat ? 'image/' . $this->_newFormat : $this->_imgData['mime'];} function getNewType() {return $this->_newFormat ? $this->_newFormat : $this->_imgData['funcType'];} }

2 comments: