English

On the fly image resizing in Lithium

Recently I’ve been playing around with the very promising PHP framework Lithium. In fact we decided to use it at work for some project as well. Documentation is a bit scarce and disorganized, and there aren’t a whole lot of content on the web about it, so I figured i might as well share some of my discoveries.

This is not a full tutorial, I am building on the source for Lithiums creator Nate Abeles photoblog example and just exending it to handle on the fly resizing.
Note: ImageMagick is required!

The reason ImageMagick is used and not GD is because of several reasons:

  • GD requires you to unpack and read the entire image into memory in the PHP process. For big images this will be a memory hog. (can be several gigabytes for 10-20M jpgs)
  • GD has an awkward format where ImageMagick is a more straight forward readable one liner
  • ImageMagick scales better in terms of file type support. (Supports more than 100 major image formats)

The only reason to use GD in my view is if you are on shared hosting and absolutely can not get ImageMagick available.

To the code

Open app/config/routes.php and you find this route handling the image requests and find this code somewhere in it:

We will change this so we achieve the following effect:

  • Map images to /image/{id}.{type}
  • Cache images to files even original is in MongoDB
  • The previous means copying /image/4cc9953fb6ada15424050000.jpg to webroot/image/4cc9953fb6ada15424050000.jpg
  • Allow resizing by url mapping /image/{id}_{width}x{height}.{type} which will create the file on the file system and serve it

First we will modify the route quite a bit, follow the inline comments in this gist:

Now you have some code that matches image urls and fires up a closure that tries to get a version of the image from the Photo model. But we don’t have that method yet, so lets create it!

With that code hooked into your model you should be up and running. Try uploading an image and accessing it using the correct pattern. Simply build an image tag like this in your views: <img src=”/image/4cc9953fb6ada15424050000_300x300.jpg” alt=”My image” />

Now this code is not perfect, it lacks quite a lot of error detection and file type support, but combined with the photo blog setup its a robust start to an image handling system. Also, there are other strategies for solving this, and the first that came to mind was to filter access to image file endings registered in the Media class and handle it on the fly at render time. The reason i chose this method instead of that is that while on the fly resizing is great, there are many situations where you know all the way that you need a set of fixed sizes. In such scenarios its probably better to be able to call the version() method from other places.

About Raymond Julin

Lead developer at Keyteq Labs, a product business in Bergen, Norway with a reputation for modern user friendly solutions.

7 Comments

  1. I’d always limit the width/height combinations you allow. Otherwise an infinite number of versions and therefore files can be created by requesting an image varying the dimensions parameters.

    • Raymond Julin

      You are right, it would be way too easy to take down this system by flooding the disk and creating load through IM. Even though the example is naive — and should be for simplicity — I should have mentioned this.

      Creating a 1000000000×100000000 image would cause some nice crashes for sure.

  2. @^/image/(?P[0-9a-f]{24})(_(?P[0-9]*)x(?P[0-9]*)?)\.(?[a-z]{2,4})@

    any idea why pattern dont pass this string

    /image/51a63497d1fce.jpg

    • Raymond Julin

      Yeah, due to the {24} part, this states that the id should be of 24 characters length.
      Besides, the route you are looking at is the one that handles the URLs with width/height embedded in it.
      The other regex, for just serving the original image is: ‘/image/{:id:[0-9a-f]{24}}.jpg’

      Any way, you need to replace the {24} with for example +, that way it’ll work with non MongoIds

  3. Tadas

    I love you! great tut.

Trackbacks / Pings

Leave a Reply