Skip to page content or skip to Accesskey List.
Search evolt.org
evolt.org login: or register

Work

Main Page Content

Automated Creation of Thumbnails With PHP

Rated 4.16 (Ratings: 10) (Add your rating)

Log in to add a comment
(18 comments so far)

Want more?

 
Picture of gvtulder

Gijs van Tulder

Member info | Full bio

User since: February 05, 2002

Last login: September 17, 2007

Articles written: 6

If you are using a Content Management System to manage the content your site, you will run into some problems very early. Publishing images with your articles will soon be the only task not fully automated. You still have to create thumbnails, convert them to the right file type or resize them manually. When, in a later redesign of your site, the requested image size changes, you will have to convert them all over again. Web developers are lazy, so it is time for an easy-to-use solution.

The Idea

The idea is simple, yet powerful: supply an image once; retrieve it in all possible formats, sizes and file types. We will build a PHP script that will automate these actions for us. We would like to use the URL of the image to supply our wishes to the script. A possible URL could be: http://www.example.com/img.php?f(3cb7f702a5967)+w(300) Using this URL, we would like to get the image identified by 3cb7f702a5967, resized to a width of 300 pixels and the corresponding height. The possibilities of our newly created script will be:

  1. resize an image to a given width and/or height, possibly preserve the aspect ration;
  2. accept new image sizes as absolute values, and as percentages of the original size;
  3. resize an image when width or height exceed a given limit;
  4. return the image as a PNG or JPEG-file.

What We Need

To build and use this script, you will need PHP compiled with the GD library and JPEG-support.

The Syntax

For telling our script what to return, we will use the following syntax. The arguments are delimited by one or more +-es. An example URL could be: http://www.example.com/img.php?f(3cb7f702a5967)+w(300)

CodeMeaning
f(3cb7f702a5967)The 13-character filename of the requested image. The images are saved without an extension, with the name defined by the current value of uniqid();. That way, new uploaded files will automatically have an unique id.
w(123) or w(10%)The wanted width of the returned image, either in absolute pixels or relative to the original size.
h(123) or h(10%)Same as above, but this is for the height of the image. When just one of the size-commands are given, the other size is automatically calculated, so that de aspect ratio of the image stays the same.
x(123)Defines the maximum width of the returned image. Resizing only takes place if the original width exceeds the given maximum. Only absolute values are accepted.
y(123)Same but for height.
t(png|jpg)Defines the requested file type of the image. If no type is given, the image is returned as the original saved type.
q(100)Only for JPEG. Asks for a specific quality of the returned image. Quality can vary from 0 to 100.

The Script

Now that we have defined the syntax of the script, it is time to take a closer look at the actual code.

Checking the arguments

First of all, the given arguments are read from the query string by a simple preg. The regular expression returns all possible tags. Then the tags have to be checked for incorrect values. Defining an array of possible tags and corresponding regular expressions, is an easy way to check the tags in a for-loop. The checked and correct tags are saved in an associative array for later use. As a last check, the script verifies that a filename is given and that that file really does exist.

// define the base image dir
$base_img_dir = "./img/";

// find tags
preg_match_all("/\+*(([a-z])\(([^\)]+)\))\+*/", $QUERY_STRING,
                $matches, PREG_SET_ORDER);

// empty array and set regular expressions for the check
$tags = array();
$check = array( "f" => "[0-9a-zA-Z]{13}",
                "w" => "[0-9]+%?",
                "h" => "[0-9]+%?",
                "x" => "[0-9]+",
                "y" => "[0-9]+",
                "t" => "jpg|png",
                "q" => "1?[0-9]{1,2}" );

// check tags and save correct values in array
for ($i=0; $i 

Loading the Image

The next step is the actual loading of the image. The getimagesize(); function is used to determine the file type, and then the correct method is used to load the image. In case the requested type of the returned image is not given, the file type of the original image is set as a default.

// retrieve file info
$imginfo = getimagesize($base_img_dir.$tags["f"]);

// load image
switch ($imginfo[2]) { 
    case 2:     // jpg
        $img_in = imagecreatefromjpeg($base_img_dir.$tags["f"]) or notfound();
        if (!isset($tags["t"])) {
            $tags["t"] = "jpg";
        }
        break;
    case 3:     // png
        $img_in = imagecreatefrompng($base_img_dir.$tags["f"]) or notfound();
        if (!isset($tags["t"])) {
            $tags["t"] = "png";
        }
        break;
    default:
        notfound();
}

Possible resize

The most important part of the script is, of course, the resize. First, we have to look whether or not a resize is needed. When width or height tags are given, the new width and height are calculated using the size of the original image. The new size is used to copy the original image to the new, resized instance.

// check for maximum width and height
if (isset($tags["x"])) {
    if ($tags["x"] 

Returning the image

The last step in our script is the actual returning of the image. The image and corresponding headers are returned as set in the query string. If the wanted type is not given, the image is returned as the original type of the saved file.

// check for a given jpeg-quality, otherwise set to default
if (!isset($tags["q"])) {
    $tags["q"] = 75;
}

// returning the image
switch ($tags["t"]) {
    case "jpg":
        header("Content-type: image/jpeg");
        imagejpeg($img_out, "", $tags["q"]);
        exit;
    case "png":
        header("Content-type: image/png");
        imagepng($img_out);
        exit;
   default:
        notfound();
}

What We Did

Now we have a script that fulfils the tasks we described above. Images are uploaded once and can be resized to whatever you want, without manual action. For the editors, the users of your CMS, uploading images is very simple. They upload the file in the size and type they want, and the script takes care of the resizing and converting. Without more work, your site can show thumbnails in many different sizes. If you, in the near future, would like to redesign your site, it is easy to get a new image size, without having to resize your whole archive.

The complete script discussed above, is available for download here. A demo of the script is running here, change the arguments in the query string to try the different commands like w() and t().

Where to go from here

Some thoughts about possible extensions of this script:

  • Enabling GIF-output. Using the command-line pngtopnm and pmmtogif programs, you can convert PNG-images to GIF on the fly. That way, you do not have to worry about older browsers not supporting the PNG-format.
  • Uploading of files in more formats. For the users of your CMS, it would be very easy if they could upload images as GIF, Windows bitmaps and TIFF. Using open source conversion utilities, you can still save these images as PNG of JPEG after uploading.
  • Using normal filenames for the images. Filenames generated with uniqid(); are easy and unique, but not very descriptive.
  • Saving file information like default ALT-tags in a database. You can easily find images for use in articles.

Gijs van Tulder is a Dutch student. He likes to play with all things web related and fancies himself as a part-time amateur web developer.

Demo

Submitted by luminosity on May 8, 2002 - 06:25.

The percentage resizing doesn't appear to be working in the demo you have up. apart from that, this is overall a pretty good article, however it would be nice to see a followup constructing the image database program, since making this script before the program to handle that feels like putting the cart before the horse. After all, without the database, how do you keep track of images with weird names like these have. The other aspects of the database such as alt text are nice, but the really critical thing I feel is to have a system for uploading images and giving them nice readable descriptions.

login or register to post comments

Cool :-)

Submitted by NZJoe on May 8, 2002 - 13:59.

This is certainly something that I can use. I'm sick of content authors that use bitmaps. And the % worked when I tried the demo.

login or register to post comments

Thanks - Nice Article

Submitted by Spyder on May 8, 2002 - 19:08.

Great article - lots of useful bits of code in here... The only thing I've always been a bit concerned about when it comes to automatic generation of thumbnails is the load on the server if it has to resize images on every request. Some sort of caching method would be great but I can also see problems if users can create images of just about any size. Having said that, a favourite site of mine, www.deviantart.com seems to handle their images in a similar way - one image gets resized into many sizes and drop shadows get added on the fly. The site gets loads of traffic but I think they have enough server power to handle it...

login or register to post comments

Caching

Submitted by skunk on May 9, 2002 - 08:00.

Caching shouldn't be too hard to implement at all. Although the request could be for virtually any image proportions the vast majority of links will be permanent links on site, so it seems a waste to dynamically resize an image every time when you could create it once, save it and serve the saved copy up every time after that.

I would propose having a cachedimage/ directory somewhere where cached images are stored, with the filenames of the image identical to the query string requested. For example, the image returned for img.php?f(3cb7f702a5967)+w(300) would be saved as "f(3cb7f702a5967)+w(300)" (if the + in a file name is a problem it could be replaced with an underscore). Whenever an image is requested a file_exists() check can be made on the file in the cachedimage/ direcoty - if it exists serve it (probably with include()), otherwise generate the image dynamically, save it to the cachedimage/ direcoty and serve it to the browser.

Because you are caching images and images can potentially change it would be worth comparing the modified time of the original image with the modified time of the cache version to check if the image has been altered since the cached image was created (with filemtime()). The only other potential problem I can see is that a saboteur could deliberately load img.php?f(3cb7f702a5967)+w(301), then img.php?f(3cb7f702a5967)+w(302) and so on increasing the requested width every time. This would result in a large number of unnecessary cache files being created in your cachedimage/ directory and could lead to a potential denial of service attack. The only way I can think of of avoiding this would be to "count" requests for each specific query string and only cache the image if 5 or more requests from different IP addresses were recieved (thus preventing a single IP address requesting bogus images) - however a determined cracker could still cause mischief if they really wanted to.

login or register to post comments

Ordinary filenames and a caching idea

Submitted by gvtulder on May 11, 2002 - 10:33.

Thanks for the positive comments; it is nice to see your first article on evolt.org being appreciated. I'm thinking about the follow-up requested by luminosity.

With a small change in the regular expression used to check the image file name, you can easily use normal filenames. Just replace the existing $check-definition by the following $check-array:

$check = array( "f" => "[^)+]+",
                "w" => "[0-9]+%?",
                "h" => "[0-9]+%?",
                "x" => "[0-9]+",
                "y" => "[0-9]+",
                "t" => "jpg|png",
                "q" => "1?[0-9]{1,2}" );

As long as you place your images in the correct $base_img_dir, you can retrieve them using f(filename.png).

Sabotage when caching could also be prevented by discarding the cached images after a certain time. When an instance is not retrieved n times in, for example, a day, it is removed from the cache. That way, you can keep your cache as small as possible.

login or register to post comments

The power of Imagemagick convert

Submitted by ezra on May 14, 2002 - 07:55.

Interesting article. As an alternative to the GD library, you can use the convert utility (part of the ImageMagick package), which allows you to do all sorts of file conversion -- you can make composite images, animated gifs, add borders, text, alter contrast, etc. It can really add power to a content management system.

login or register to post comments

error in resize

Submitted by Franks on October 27, 2002 - 08:20.

you have an error in your resize function. If only height is given, the width will be wrong. Below is the correct version:
    // resize
    if (isset($tags["w"]) and isset($tags["h"])) {
        $out_w = $tags["w"];
        $out_h = $tags["h"];
    } elseif (isset($tags["w"]) and !isset($tags["h"])) {
        $out_w = $tags["w"];
        $out_h = $imginfo[1] * ($tags["w"] / $imginfo[0]);
    } elseif (!isset($tags["w"]) and isset($tags["h"])) {
        $out_w = $imginfo[0] * ($tags["h"] / $imginfo[1]);
        $out_h = $tags["h"];
    } else {
        $out_w = $tags["w"];
        $out_h = $tags["h"];
    }

login or register to post comments

Error in resize

Submitted by gvtulder on October 27, 2002 - 09:04.

Franks, you're right. I've corrected the script. It should work now. Thanks for the correction.

login or register to post comments

Quality of image when resized

Submitted by christo on October 29, 2002 - 10:08.

Why is the image quality so poor when resized?

login or register to post comments

Demo does not work here.

Submitted by jlalande on December 28, 2002 - 15:27.

When I used the demo at http://members.evolt.org/gvtulder/evolt/img/img.php?f(3cb7f702a5967), and do a resize of the width of any value or height of any value, the image appears at the right size but the entire image is black. Even if I do w(100%) the same thing: image is all black.

login or register to post comments

Demo does not work here

Submitted by gvtulder on December 31, 2002 - 04:55.

This must be an error of the GD library or the evolt.org image. I replaced the evolt.org image with another image, and that does work. http://members.evolt.org/gvtulder/evolt/img/img.php?f(3cb7f702a5967)+w(100)+h(100) now gives you normal colors.

To improve the image quality, you can use imagecopyresampled() instead of imagecopyresized() and replace imagecreate() with imagecreatetruecolor(). These functions should give you a better image. (You need PHP compiled with version 2 of the GD library to get these functions to work.)

login or register to post comments

I'm still having trouble with getting black image

Submitted by jlalande on January 11, 2003 - 16:11.

Your demo works here now. However, I'm trying to use some of your ideas to generate a thumbnail file when I upload the regular image. I plan on trying to generate them dynamically once I get static thumbnails figured out. Anyway, the code I have is:

$largeAttr = getimagesize($largeFile);
$l_w = $largeAttr[0];
$l_h = $largeAttr[1];
$src = imagecreatefromjpeg($largeFile);
$tn_w = floor($l_w / 4);
$tn_h = floor($l_h / 4);
$dst = imagecreatetruecolor($tn_w, $tn_h);
imagecopyresampled($dst, $src, 0, 0, 0, 0, tn_w, tn_h, imagesx($src), imagesy($src));
imagejpeg($dst, $thumbFile, 100);

A thumbnail image file is written to disk, but when I view it, it is all black. Any suggestions?
The version of PHP is 4.2.3 and GD 2

login or register to post comments

I only get 404s

Submitted by corbinsiddall on March 10, 2003 - 15:29.

For some reason I only get 404s when I attempt to execute the following:

http://ipaddress/img.php?f(3e6d07b80aa2d)

I really am not sure where to begin the troubleshooting. The file does exist on the server and http://ipaddress/3e6d07b80aa2d will display the image. I have compiled PHP with GD as seen here [snipit from phpinfo();]:

gd
GD Support enabled
GD Version 2.0 or higher
JPG Support enabled
PNG Support enabled
WBMP Support enabled

Any ideas?

login or register to post comments

just a dubt

Submitted by PIG on June 3, 2003 - 18:28.

hi all, can anyone tell me from which PHP version is it possible to run this script ??

login or register to post comments

Many pictures

Submitted by radar on June 13, 2003 - 11:21.

I don't really knows how the "header()" function works, so I wish someone could tell me how to use this script in this way: One page with many thumbnails, all created by PHP. I want to keep the script in a separate file (if possible)... It's probably very easy, but I'm still a beginner in how to use PHP...

login or register to post comments

Also getting either black or little blurry squares

Submitted by powerpop on April 15, 2004 - 14:20.

any idea what i am missing in the code below? i am getting 32x32 thumbnails that are either black or have a set of 16 squares, each being a part of the larger image but blurry - this is on GD2 and PHP4
<br><br>
  $src_img = imagecreatefromjpeg($path . strtolower($HTTP_POST_FILES['userfile']['name']));<br>
  $dst_img = imagecreatetruecolor($thumb_width,$thumb_height);<br>
  imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $thumb_width, $thumb_height, imagesx($src_img), imagesy($src_img));<br>
  imagejpeg($dst_img, $thumbpath . strtolower($HTTP_POST_FILES['userfile']['name']), $quality);<br>
  imagedestroy($src_img);<br>
  imagedestroy($dst_img);<br>

and how do i post code snippets like the above in evolt??

login or register to post comments

Images other than 72ppi are made but all black

Submitted by fishnyc22 on October 24, 2005 - 21:44.

If I upload an image that is anything but 72ppi, the thumbnail created is all black. Any idea why this would be happening? I can't seem to figure it out. Any help would be apprecited.

login or register to post comments

black thumbnails

Submitted by tmchny on August 7, 2007 - 23:00.

I have just spent hours trying to work out why some jpeg fils create black thumbnails when they are uploaded. In my case it was a really simple and obvious solution. I noticed that the files that were giving me problems had the file extension in uppercase and weren't being recognised by my if statement as a jpeg

login or register to post comments

The access keys for this page are: ALT (Control on a Mac) plus:

evolt.orgEvolt.org is an all-volunteer resource for web developers made up of a discussion list, a browser archive, and member-submitted articles. This article is the property of its author, please do not redistribute or use elsewhere without checking with the author.