Skip to content

Archive for December, 2010

28
Dec

How To: PHP Thumbnails – Resize images with PHP

I have revisited this subject here: Revisited: PHP Thumbnails

We all have been in situation where we allow users to upload images for content items or profile pictures. They’ll just upload huge images without considering bandwidth and disk space. Soon you have hundreds images above 2MB in size cluttering your precious harddisk space and taking up all of the bandwidth with just a few pageviews.

So how do you conquer this problem? Well, resize the images automatically. Keeping their ratio and quality but reducing their size significantly.

I assume you have some knowledge with HTML and know how to upload a file. Therefor in these examples I use the file example.png and assume its in the images/ folder.

First the class that we will use to resize the image

<?php
/**
 * Class createImage
 *
 * @author Thijs Damen
 * @desc Creates a resized image from an existing image
 *
 */
class createImage {

	/*Folder where the current image can be found*/
	private $RootFolder;

	/*Folder relative from RootFolder to write the image to*/
	private $dstFolder;

	/*Name of the image*/
	private $ImageName;

	/*Image Extension*/
	private $ImageExtention;

	/*Maximum width for image*/
	private $img_maxWidth;
	/*Maximum height for image*/
	private $img_maxHeight;

	/*Current width of the image*/
	private $img_curWidth;
	/*Current height of the image*/
	private $img_curHeight;

	/*New width of the image*/
	private $img_newWidth;

	/*New height of the image*/
	private $img_newHeight;

	/*Quality of the new image*/
	private $imgQuality;

	/*Thumb of the image*/
	private $thumb;

	/*
	 * Constructor
	 * Recieves all parameters and immediatly creates the new image
	 */
	public function __construct($rootfolder, $imagename, $dstfolder, $maxwidth, $maxheight, $quality = 100) {
		$this->RootFolder 		= 	$rootfolder;
		$this->ImageName		=	$imagename;
		$this->dstFolder		= 	$dstfolder;
									//Neat trick that will get everything from the last . as a string (aka. extension)
		$this->ImageExtention 	=   end(explode(".", $imagename));
		$this->img_maxWidth		= 	$maxwidth;
		$this->img_maxHeight	=	$maxheight;
		$this->imgQuality		=	$quality;

		$this->calcImgSize();
	}

	/*
	 * Smart function that will calculate the new width and height of the image
	 */
	private function calcImgSize() {
		$src = getimagesize($this->RootFolder.$this->ImageName);
		$this->img_curWidth 	= 	$src[0];
		$this->img_curHeight	= 	$src[1];

		//Some calculation to determine wether the layout it landscape or portrait
		if(($this->img_curWidth / $this->img_maxWidth) > ($this->img_curHeight / $this->img_maxHeight)) {
			// landscape mode so recalculate based on width
			if ($this->img_curWidth > $this->img_maxWidth) {
				$this->img_newWidth = intval($this->img_maxWidth);
				$this->img_newHeight = intval($this->img_curHeight * ($this->img_maxWidth / $this->img_curWidth));
			} else {
				$this->img_newWidth = $this->img_curWidth;
				$this->img_newHeight = $this->img_curHeight;
			}
		} else {
			// portrait mode so recaulculate on height
			if ($this->img_curHeight > $this->img_maxHeight) {
				$this->img_newHeight = intval($this->img_maxHeight);
				$this->img_newWidth = intval($this->img_curWidth * ($this->img_maxHeight / $this->img_curHeight));
			} else {
				$this->img_newWidth = $this->img_curWidth;
				$this->img_newHeight = $this->img_curHeight;
			}
		}
		//GD Function that returns an image identifier based on width and height
		$this->thumb = imagecreatetruecolor($this->img_newWidth, $this->img_newHeight);
		if (!$this->createImage()) {
			return false;
		}
	}

	/*
	 * Create the new image based on extension
	 */
	private function CreateImage() {
		/*
		 * Switch the extension. so we know what type of image we are dealing with
		 * All of these will use their respecitve create function (imagecreatefrom)
		 * Create a copy from it and write it to the disk
		 */
		switch ($this->ImageExtention) {
			case "jpg":
			case "jpeg":
				header("Content: image/jpeg");
				$imgcreate = imagecreatefromjpeg($this->RootFolder.$this->ImageName);
				$imgcopy = imagecopyresampled($this->thumb, $imgcreate, 0,0,0,0, $this->img_newWidth, $this->img_newHeight, $this->img_curWidth, $this->img_curHeight);
				return imagejpeg($this->thumb,$this->RootFolder.$this->dstFolder.$this->ImageName,$this->imgQuality);
			break;
			case "png":
				header("Content: image/png");
				$imgcreate = imagecreatefrompng($this->RootFolder.$this->ImageName);
				$imgcopy = imagecopyresampled($this->thumb, $imgcreate, 0,0,0,0, $this->img_newWidth, $this->img_newHeight, $this->img_curWidth, $this->img_curHeight);
				/*
				 *  This is a special case. Where imagejpeg and imagegif take their quality parameter as 0 to 100.
				 *  0 being the worst and 100 being best. imagepng expects 0 to 9, 0 being the best. 9 being the worst.
				 *  So we have to do some calculation for the quality
				 */
				$quality = ($this->imgQuality - 100) / 11.11;
				$this->imgQuality = round(abs($quality));
				return imagepng($this->thumb,$this->RootFolder.$this->dstFolder.$this->ImageName, $this->imgQuality);
			break;
			case "gif":
				header("Content: image/gif");
				$imgcreate = imagecreatefromgif($this->RootFolder.$this->ImageName);
				$imgcopy =  imagecopyresampled($this->thumb, $imgcreate, 0,0,0,0, $this->img_newWidth, $this->img_newHeight, $this->img_curWidth, $this->img_curHeight);
				return imagegif($this->thumb,$this->RootFolder.$this->dstFolder.$this->ImageName,$this->imgQuality);
			break;
		}
	}
}

How to use this class? Simple:

I have an image in the images/ folder named image.png. It is 4096×3072 pixels in size (and 1.4MB big) currently and I want to resize it to a maximum of 512×512.

new createImage("images/", "image.png", "thumbs/", 512, 512, 80);

This will now create a image in images/thumbs/, still named image.png of size: 512*384 with a little less quality then the original, but little chance you will actually notice the difference (also, it is only 56KB big, which is 2500% smaller(!) then the original).

The best part about this. I can do the same with my JPEGs and GIFs without a hassle. look

new createImage("images/","image.jpg", "thumbs/", 512, 512, 80);

And this will do exactly the same just for the JPG extension. So instead of having to remember 3 different functions, I can just use a single class which holds all the logic for me.

There are some prerequisites. The destFolder has to be writable and you have to have the GD library installed. Also, it might be necessary to temporarily increase the memory limit of php.

ini_set('memory_limit', '256M');

I hope this helps someone with resizing their images in a good way. Keeping proportions and reducing image size significantly.

20
Dec

How To: Create your own Content Distribution Network

There are many Content Distribution Networks (CDN) aka Content Delivery Networks out there so why create your own? I for one argue that most CDNs out there don’t host enough code.

Here’s a list that google supports:

Chrome Frame
Dojo
Ext Core
jQuery
jQuery UI
MooTools
Prototype
script.aculo.us
SWFObject
Yahoo! User Interface Library (YUI)
WebFont Loader

This may seem as a long list, but what if you use jQuery plugins like jQuery validate or Fancybox often?

Why not create your own CDN system to host the code you use the most (You can even choose to use googleapis for all the libraries that are available and use your own CDN for the ones that are not). Personally, I prefer that option.
Another benefit of hosting your own CDN is that if you create web applications using a in-house made CMS you can also host its icon-sets and other frequently accessed static images.

The reason’s to use a CDN are simple and straight-forward:

Using a CDN will save you bandwich
Using a CDN will greatly increase your scripts load speed

So how do you create your own CDN?

#1 Setup the domain

If it is possible create a new domain name and disable the use of cookies. Why? RavelRumba explain it in this post. If you can not afford, or do not want to pay for a new domain you can consider creating a subdomain and then making sure cookies are only saved on the www.* domain.

#2 Figure out a filetree structure

Your filetree is very important when setting up your CDN. Yes, you can simply put every file you need in the root directory of the webserver but this is clumbersome and after some time will become confusing.
Google uses the following syntax:

http://ajax.googleapis.com/ajax/libs/library/version/filename
or
http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.js

You can also use this syntax or use a more simplified version. Which I personally prefer since the code you should host on your CDN often does not come with that many versions.

http://yourwebsite.com/library/filename

http://yourwebsite.com/library/plugins/pluginname/filename

http://yourwebsite.com/plugins/pluginname/filename

http://yourwebsite.com/images/imagename

I then use the library folder to host specific libraries (and their specific plugins), the plugins folder to host plugins (such as modernizr) and the image folder to host my frequently used images.

#3 Making it perform

As said before, you should disable cookies on the domain of your choice.

Because you are using a CDN you can use a custom .htaccess file to optimise the content distrubution. Set far-future (the content is static anyway) expire headers and deflate any content delivered.

.htaccess example (mostly stripped from the –awesome- html5boilerplate created by the, maybe even more awesome Paul Irish

#Set proper content-types for your files
# Proper svg serving.
AddType     image/svg+xml              svg svgz
AddEncoding gzip                       svgz           

# webfonts
AddType application/vnd.ms-fontobject  eot
AddType font/truetype                  ttf
AddType font/opentype                  otf
AddType font/woff                      woff          

# assorted types
AddType image/vnd.microsoft.icon       ico
AddType image/webp                     webp
AddType text/cache-manifest            manifest
AddType text/x-component               htc
AddType application/x-chrome-extension crx

# gzip compression.
<IfModule mod_deflate.c>
  AddOutputFilterByType DEFLATE text/html text/plain text/css application/json
  AddOutputFilterByType DEFLATE text/javascript application/javascript application/x-javascript
  AddOutputFilterByType DEFLATE text/xml application/xml text/x-component
  <FilesMatch "\.(ttf|otf|eot|svg)$" >
    SetOutputFilter DEFLATE
  </FilesMatch>
</IfModule>

<IfModule mod_expires.c>
  Header set Cache-Control "public"
  ExpiresActive on

# Perhaps better to whitelist expires rules? Perhaps.
  ExpiresDefault                          "access plus 1 month"

# cache.manifest needs re-requests in FF 3.6 (thx Remy ~Introducing HTML5)
  ExpiresByType text/cache-manifest       "access plus 0 seconds"

# favicon (cannot be renamed)
  ExpiresByType image/vnd.microsoft.icon  "access plus 1 week" 

# media: images, video, audio
  ExpiresByType image/gif                 "access plus 1 month"
  ExpiresByType image/png                 "access plus 1 month"
  ExpiresByType image/jpg                 "access plus 1 month"
  ExpiresByType image/jpeg                "access plus 1 month"
  ExpiresByType video/ogg                 "access plus 1 month"
  ExpiresByType audio/ogg                 "access plus 1 month"
  ExpiresByType video/mp4                 "access plus 1 month"
  ExpiresByType video/webm                "access plus 1 month"

# webfonts
  ExpiresByType font/truetype             "access plus 1 month"
  ExpiresByType font/opentype             "access plus 1 month"
  ExpiresByType font/woff                 "access plus 1 month"
  ExpiresByType image/svg+xml             "access plus 1 month"
  ExpiresByType application/vnd.ms-fontobject "access plus 1 month"

# css and javascript
  ExpiresByType text/css                  "access plus 1 week"
  ExpiresByType application/javascript    "access plus 1 week"
  ExpiresByType text/javascript           "access plus 1 week"
</IfModule>

# Since we're sending far-future expires, we don't need ETags for
# static content.
FileETag None

# use utf-8 encoding for anything served text/plain or text/html
AddDefaultCharset utf-8

# force utf-8 for a number of file formats
AddCharset utf-8 .html .css .js .xml .json .rss

#4 All done

It’s that simple to create a very basic CDN. Obviously there are other more advanced topics we could discuss about how to optimize the distribution of content, but this is in essence a CDN that does what it promises.

17
Dec

Hello Blog

 ('&%:9]!~}|z2Vxwv-,POqponl$Hjig%eB@@>}=<M:9wv6WsU2T|nm-,jcL(I&%$#"
 `CB]V?Tx<uVtT`Rpo3NlF.Jh++FdbCBA@?]!~|4XzyTT43Qsqq(Lnmkj"Fhg${z@>

or HELLO WORLD!

Starting this blog thanks to Scott Hanselman amazing talk at Wintellect’s Devscovery where he says that Every Developer Needs a Blog. Well, I consider myself a developer so here’s my blog.

For my very first blog post I’ll just leave you with a function that I myself use atleast 15 times a day. Now I know it is bad practice to be outputting data to the screen (instead of using actual logging functions), But this one is just to good.

<?php
function print_pre($array) {
    echo '<pre>';
    print_r($array);
    echo '</pre>';
}

Simple, but effective.