Generate thumbs by visiting a URL such as your.com/thumbs/images/image.50x50.jpg. This will create a 50x50px thumbnail of your.com/images/image.jpg.
The thumb will be stored on your server at your.com/thumbs/images/image.50x50.jpg so the next request for the same image will be loaded without loading php for ultra fast image cache.
Introduction
About a year ago I came across a fantastic script called phpThumb. It is an open source project used to resize images. Sure you can do the same thing with tools such as GD2 or imagemagick (or magickwand), however its much nicer to not have to worry about those things and just focus on getting the right image with ease.
It was as easy as
<img src="phpthumb/phpThumb.php?src=myimage.jpg&w=100&h=100">
The problems started to arise on high-volume servers when apache had to get PHP to parse the phpThumb code for every image requested. Sure it has caching but it still has to load PHP to decide if it should use the cache or not.
In the past I have seen this issue solved using mod_rewrite to redirect non-existent images to a script where they can be generated. As a proof-of-concept I will provide the basic information required to get this running.
What you need
- Apache
- mod_rewrite
- PHP
These things usually come with dedicated and shared hosting servers by default, however installation is beyond the scope of this article.
Ok, just tell me how to do it!
Upload phpThumb
Download phpThumb from here:
http://phpthumb.sourceforge.net/
Upload phpThumb to yoursite.com/phpthumb
Setup Mod_Rewrite
Create yoursite.com/thumbs/.htaccess
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?thumb=$1 [L,QSA]
</IfModule>
Create the Thumbnail Generator
Create yoursite.com/thumbs/index.php
* Create a thumbnail
*
* @author Brett @ Mr PHP
*/
// define allowed image sizes
$sizes = array(
'50x50',
'100x100',
'250x250',
);
// ensure there was a thumb in the URL
if (!$_GET['thumb']) {
error('no thumb');
}
// get the thumbnail from the URL
$thumb = strip_tags(htmlspecialchars($_GET['thumb']));
// get the image and size
$thumb_array = explode('.',$thumb);
$image = '../';
foreach($thumb_array as $k=>$thumb_part){
if ($k != count($thumb_array)-2) {
$image .= $thumb_part . '.';
}
}
$image = substr($image,0,-1);
$size = $thumb_array[count($thumb_array)-2];
list($width,$height) = explode('x',$size);
// ensure the size is valid
if (!in_array($size,$sizes)) {
error('invalid size');
}
// ensure the image file exists
if (!file_exists($image)) {
error('no source image');
}
// generate the thumbnail
require('../phpthumb/phpthumb.class.php');
$phpThumb = new phpThumb();
$phpThumb->setSourceFilename($image);
$phpThumb->setParameter('w',$width);
$phpThumb->setParameter('h',$height);
//$phpThumb->setParameter('far','C'); // scale outside
//$phpThumb->setParameter('bg','FFFFFF'); // scale outside
if (!$phpThumb->GenerateThumbnail()) {
error('cannot generate thumbnail');
}
// make the directory to put the image
if (!mkpath(dirname($thumb),true)) {
error('cannot create directory');
}
// write the file
if (!$phpThumb->RenderToFile($thumb)) {
error('cannot save thumbnail');
}
// redirect to the thumb
// note: you need the '?new' or IE wont do a redirect
header('Location: /thumbs/'.$thumb.'?new');
// basic error handling
function error($error) {
header("HTTP/1.0 404 Not Found");
echo '<h1>Not Found</h1>';
echo '<p>The image you requested could not be found.</p>';
echo "<p>An error was triggered: <b>$error</b></p>";
exit();
}
//recursive dir function
function mkpath($path, $mode){
is_dir(dirname($path)) || mkpath(dirname($path), $mode);
return is_dir($path) || @mkdir($path,0777,$mode);
}
Test it out!
Upload an image to yoursite.com/images/myimage.jpg
Open your web browser to yoursite.com/thumbs/images/myimage.100x100.jpg
Check in your thumbs folder, the file should actually be there now. Next time it is requested PHP will not be loaded.
Clearing Cache
This is a small script I use to delete all the cached thumbs.
Upload an image to yoursite.com/thumbs/flush.php










Agus MU
GREAT! I love it!
Possible Security Issue
I had something similiar implemented in one of my projects but removed it
later because this may open up your webapp for (D)DoS attacks (which are farely seldom).
Imagine somebody requesting these:
yoursite.com/thumbs/images/myimage.100x100.jpg
yoursite.com/thumbs/images/myimage.100x101.jpg
yoursite.com/thumbs/images/myimage.100x102.jpg
yoursite.com/thumbs/images/myimage.100x103.jpg
and so on...
Every time the image is parsed, loaded into memory (MEM), resized (CPU)
and stored (IO, DISKSPACE). A workaround is to define a set of fixed dimensions e.g.
'small' => array(50,50), 'large' => array(500,500) and allow only those to
be used.
- David Persson
thanks for the security heads up
Thanks for the feedback on the security issue.
I added an $allowed array to ensure only the specified sizes can be created.
images that go multi-levels deep
I've got this script working, but I'm having trouble with files that are multiple folders deep like:
http://server.com/thumbs/index.php?thumb=/projects/gallery_albums/type/n...
Presumably it's trying to mkdir (/projects/gallery_albums/type/). Is there a way I can work around this?
Thanks Brett, great script. I just wish phpthumb would solve this in some way, it's a huge pain that the cache is so slow.
images that go multi-levels deep
It should work fine, although if you have mod-rewrite the URL should be:
http://server.com/thumbs/projects/gallery_albums/type/numbers_cell_03.10...
At the end of the script, what is in $error ?
Also, what is the value of $thumb and $image ?
thanks
Just tried this with a few modifications and it's working lovely wtih ExpressionEngine. Thanks Brett
Errors
Hello,
First of all - thanks for this function.
Anyway - I figured out some errors/enhancements:
1. It is:
if (!in_array($size,$sizes)) {
should be:
if (!in_array($size,$allowed)) {
2. You should quit script on the first error - or the thumb will be generated no matter there is an error. I've created a function:
function exit_on_error($error) {
header("HTTP/1.0 404 Not Found");
echo 'Not Found';
echo '
The image you requested could not be found.
';
echo "
An error was triggered: $error
";
}
and you should use it on each error:
if (!$_GET['thumb']) {
die (exit_on_error('no thumb'));
}
3. And little more security on input:
// get the thumbnail from the URL
$thumb = strip_tags(htmlspecialchars($_GET['thumb']));
errors fixed
i have fixed the bug with the allowed sizes, added an error function so that the script exits correctly on an error, and added the input security.
thanks for taking the time to point out these issues.
Typos
Couple of typos I found when setting up:
In the following block:
// note: you need the '?new' or IE wont do a redirect
header('Location: /thumbs/'.$thumb.'?new')
You forgot a semicolon on the end.
Also, in your instructions it says to create a directory called "thumbs", and refers to that directory all the way through, but the code refers to "thumb", and for me it only worked with a directory called "thumb".
That aside - what fantastic work! We've just launched a site which used phpthumb heavily, and it crashed the server hard. Hoping that this may help us reduce the load enough that it can cope.
thanks
Hi Jaymis, Thanks for the tip, I have added the semicolan.
The script should work in any folder. You should be able to name it whatever you like. The only thing forcing it to be /thumbs/ is the redirect in the line you pointed out.
Document root
Nice script, one main modification... if using this with certain other modrewrite settings (e.g. replacing php files with fake directories etc) the include needs to be relative from the document root, rather than relative from the current page.
To do this replace the line :
require('../phpthumb/phpthumb.class.php');
with the more generic:
require($_SERVER['DOCUMENT_ROOT'] . '/phpthumb/phpthumb.class.php');
Hope that helps
Works on first thumb, fails on others
Thanks for your post, it's just what the doctor ordered. I use phpthumb within expressionengine and have noticed as I pile more sites onto my shared server, my gpu count is rising rapidly per month. Having a few issues though.
When I first run the script I see this error:
The image you requested could not be found.
An error was triggered: cannot save thumbnail
If I refresh again the thumbnail shows up correctly. If I try and do this for another image (now that the directory structure has been created from the previous image), this error is displayed:
Not Found
The image you requested could not be found.
An error was triggered: cannot create directory
I'm running wamp on windows 7, but have also found the same errors on the production server. If I delete the file and directories created for the first thumbnail, I can recreate above errors. Any ideas where I might be going wrong?
error messages
If it cannot save the thumbnail then $phpThumb->RenderToFile($thumb) is failing. I assume it is to do with your mkdir not creating the full path. Perhaps it is just creating 1 folder per run. So if you have an image 3 folders deep it would take 3 refreshes to generate. Would you be able to confirm this?
To remove the second error (Warning mkdir), just put an @ in front of mkdir. I have now done this in the demo code above.
Refreshing not impressing
So if you have an image 3 folders deep it would take 3 refreshes to generate. Would you be able to confirm this?
No luck, no matter how many times I refresh is still gives me the cannot create directory error. I'm running php 5.2.9-2
To remove the second error (Warning mkdir), just put an @ in front of mkdir. I have now done this in the demo code above.
Confirmed it fixes the 2nd error. Thanks!
more ideas..
Hi Rick,
Does it create the folder to put the image in?
If YES:
The problem is the phpThumb is failing to render the image. print_r($phpThumb) may give some insight.
if NO:
not the problem is the mkdir is failing. www.php.net/mkdir
Fix
Found a fix for cannot create dir.
Replace:
if (!@mkdir(dirname($thumb),0777,true)) {
error('cannot create directory');
}
With:
function mkdir_recursive($pathname, $mode)
{
is_dir(dirname($pathname)) || mkdir_recursive(dirname($pathname), $mode);
return is_dir($pathname) || @mkdir($pathname,0777,$mode);
}
if (!mkdir_recursive(dirname($thumb),true)) {
error('cannot create directory');
}
Should work fine now.
thanks for the fix, i have
thanks for the fix, i have added it to the example.
can't create directory
I've created the thumb directory and set the permissions to 0777 but I always get the error "cannot create directory"
I have an already existing library of images located at /gallery/images and then cataloged by numbered directory. So an image I was trying to test for example is located at /gallery/images/2/2.jpg
When I enter /thumb/gallery/images/2/2.50x50.jpg I get the cannot create directory error
It should create the folder /thumb/gallery/images/2/ and place the thumb in there correct?
I copied phpThumb to /phpthumb, created the /thumb directory with the .htaccess file and index.php file inside and chmod it 0777. Is there a config step I missed? FWIW I have been using phpThumb for quite a while with no problems, besides the performance issues you described which is why I'd love to get this working :)
Render to file error accident fixed
Thanks a lot for this script. Only thing you need to edit is this line:
// write the file
if ($phpThumb->RenderToFile($thumb)) {
error('cannot save thumbnail');
it should actually be this:
// write the file
if (!$phpThumb->RenderToFile($thumb)) {
error('cannot save thumbnail');
the way you had it would give every image a cannot save thumbnail error message before it, saved the thumbnail. Also I'd like to say that the mkdir fix works.
thanks, i have fixed this in
thanks, i have fixed this in the example.
Thanks so much for this, the
Thanks so much for this, the answer to many hours of hair loss!!! Works a treat.
Post new comment