I've been playing around with a new website that will allow members to upload some content and images. I've knocked up the code html, php & mysql (with the help of google) and it now all works but not fast.
It could be my code is inefficient or my hosting company is restricting the uploading speed ... or both. I am very new to developing websites!
Using small size images <300kb is not an issue, but as soon as I use 6mb ones its can take up to 5minutes to upload the images even though I am reducing there size before upload. I've tested without database inserts so I know its the images part thats causing the issue.
Before I start looking at implementing DropBox to store the images ... has anyone come across this problem before that could recommend a different approach?
Code html snippet:
<form name="add-form" action="includes/new_post.php" method="post" enctype="multipart/form-data">
<input id='id_question_pic' name="upfile1[]" type="file" tabindex="3" multiple accept='image/*' max-uploads=6 />
Code in php:
//put all the uploaded images into an array
$files=array();
$fdata=$_FILES['upfile1'];
for($i=0;$i<count($fdata['name']);++$i){
$files[]=array(
'name' =>$fdata['name'][$i],
'type' => $fdata['type'][$i],
'tmp_name'=>$fdata['tmp_name'][$i],
'error' => $fdata['error'][$i],
'size' => $fdata['size'][$i]
);
}
//move to the correct directory, with unique file names
$directory = 'C:\Inetpub\vhosts\mydomain\form_uploads\\'; //use local server path (hosting company)
$dbimagepath = 'form_uploads/';
$result = true;
foreach ($files as $file) {
if ($file['error'] == 0) {
$filename = $file['name'];
if (strlen($filename) > 20) {$filename = substr($filename, strlen($filename) - 8);}
$filename = mt_rand() . '_' . $filename;
//ensure the filename is unique//
while (#getimagesize($directory . $filename)){$filename = mt_rand() . $filename;}
$fullpath = $directory . $filename;
if (exif_imagetype($file['tmp_name'])== 2){
$image = imagecreatefromstring(file_get_contents($file['tmp_name']));
//ORIGINAL DIMENTIONS
list( $width , $height ) = getimagesize($file['tmp_name']);
//ORIGINAL SCALE
$xscale=$width/600;
$yscale=$height/600;
//NEW DIMENSIONS WITH SAME SCALE
if ($yscale > $xscale)
{
$new_width = round($width * (1/$yscale));
$new_height = round($height * (1/$yscale));
}
else
{
$new_width = round($width * (1/$xscale));
$new_height = round($height * (1/$xscale));
}
//NEW IMAGE RESOURCE
if(!($imageResized = imagecreatetruecolor($new_width, $new_height)))
{//error handling}
//RESIZE IMAGE
if(! imagecopyresampled($imageResized, $image , 0 , 0 , 0 , 0 , $new_width , $new_height , $width , $height))
{//error handling}
$image = $imageResized;
$exif = exif_read_data($file['tmp_name']);
if (!empty($exif['Orientation']) || !$exif['Orientation']===null){
switch($exif['Orientation'])
{
case 3: // 180 rotate left
$image=imagerotate($image, 180, -1);
break;
case 6: // 90 rotate right
$image=imagerotate($image, -90, -1);
break;
case 8: // 90 rotate left
$image=imagerotate($image, 90, -1);
break;
default:
break;
}
}
if (imagejpeg($image, $fullpath, 80)){
//call function to update the database
}
imagedestroy($image);
}
else
{$result = false;}
}
}
Take a look at 3rd party solutions that are build to solve your particular problem.
Uploadcare for example.
With it you can have fast reliable uploads even without access to your host's filesystem and upload speed is limited only by your visitor's internet connectivity.
I have a function which resizes images and whilst it works fine on my test server, it doesn#t work on the new live server. The error message i get is
Warning: imagejpeg(): Unable to open ' /home/sites/public_html/images/2013-24-1-240x300.jpg' for writing: No such file or directory in /home/sites/public_html/includes/functions/html_output.php on line 352
line 352 in the code below is the imagejpeg line near to the bottom. If its creating the image on the fly, I dont understand why its trying to open the file.
Images folder is writeable (changed to 777 to check) as the original image is showing fine. GD is enabled on the live server bundled (2.0.34 compatible)
Both versions of PHP and GD are the same now on both servers. Only difference is test server is running WAMP and live is a linux.
function image_resample($src,$width,$height) {
define(JPEGQUALITY, 75);
define(ALLOWSQUASH,0.10);
if ($src=='') {
return $src;
}
$i = #getimagesize( $src ); // 1-gif (ignore), 2-jpeg, 3-png
if (!(($width == SMALL_IMAGE_WIDTH) && ($height == SMALL_IMAGE_HEIGHT)) &&
!(($width == MEDIUM_IMAGE_WIDTH) && ($height == MEDIUM_IMAGE_HEIGHT))&&
!(($width == LARGE_IMAGE_WIDTH) && ($height == LARGE_IMAGE_HEIGHT))) {
return $src; // can amend to work with other images
}
if (!( ($i[2] == 3) || ($i[2] ==2))) {
return $src;
}
$file = preg_replace( '/\.([a-z]{3,4})$/i', "-{$width}x{$height}.\\1", $src ); // name of resampled image
if (is_file( $file ) ) {
return $file;
}
$scr_w = $i[0];
$scr_h = $i[1];
if (($scr_w * $scr_h * $width * $height) == 0) {
return $src;
}
$howsquashed = ($width / $height * $scr_h / $scr_w);
if (((1 / (1 + ALLOWSQUASH)) < $howsquashed) && ($howsquashed < (1 + ALLOWSQUASH))) $simpleway='true';
$scalefactor = min($width/$scr_w, $height/$scr_h);
$scaled_w = (int)($scr_w * $scalefactor);
$scaled_h = (int)($scr_h * $scalefactor);
$offset_w = max(0,round(($width - $scaled_w) / 2,0));
$offset_h = max(0,round(($height - $scaled_h) / 2));
$dst = DIR_FS_CATALOG . '/' . $file;
$dstim = #imagecreatetruecolor ($width, $height);
$background_color = imagecolorallocate ($dstim, 255, 255, 255);
imagefilledrectangle($dstim, 0, 0, $width, $height, $background_color);
if ( $i[2] == 2) {
$srcim = #ImageCreateFromJPEG ($src); // open
}
elseif ( $i[2] == 3) {
$srcim = #ImageCreateFromPNG ($src);
}
if ($simpleway == 'true') {
imagecopyresampled ($dstim, $srcim, 0, 0, 0, 0, $width, $height, $scr_w, $scr_h);
}
else {
$intim = #imagecreatetruecolor ($width, $height);
imagecopyresampled ($intim, $srcim, $offset_w, $offset_h, 0, 0, $scaled_w, $scaled_h, $scr_w, $scr_h);
imagecopy ( $dstim, $intim, $offset_w, $offset_h, $offset_w, $offset_h, $scaled_w, $scaled_h);
imagedestroy ($intim);
}
if ( $i[2] == 2) {
imagejpeg ($dstim , $dst , JPEGQUALITY);
}
elseif ( $i[2] == 3) {
imagepng ($dstim , $dst);
}
imagedestroy ($srcim);
imagedestroy ($dstim);
return $file; // Use the newly resampled image
}
the message is pretty clear
Warning: imagejpeg(): Unable to open ' /home/sites/public_html/images/2013-24-1-240x300.jpg'
for writing: No such file or directory in
/home/sites/public_html/includes/functions/html_output.php on line 352
if the directory exists chmod it so the apache user can write to it.
if the directory doesnt exist create it and then chmod it (644 should be enough)
if the file exists already - it can not over write it, So chmod the file (its likely you used SCP to transfer it and its under the user you SCP'ed as) ls -l to confirm the permissions
I am trying to use adaptive images and I have no problem using it with normal linked images that end in .jpg, .png, .gif but when I use a filesystem that a php file grabs a unextensioned file from a directory and uses header Content-Disposition: inline to display the image, the htaccess does not properly run it through the php file.
Here is the Rewrite rule that works for normal linked images:
RewriteRule \.(?:jpe?g|gif|png)$ adaptive-images.php [NC,L]
Here is an example of a image link that needs to be run through adaptive-images.php:
http://localhost/projects/file-system/getmedia.php?id=2&slug=35dq
How do I write another rewrite rule that will take care of those images? I am currently using this rule but it just makes broken images (I have tried some other rules but no difference):
RewriteRule ^getmedia.php$ adaptive-images.php [NC,L]
Although my htaccess rule was maybe at fault, the adaptive images php script does not accommodate php fed images natively.
The htaccess rule I used was:
RewriteCond %{QUERY_STRING} !(^|&)noadaptive=(true)(&|$) [NC]
RewriteRule ^getmedia\.php.*$ adaptive-images.php [NC,L]
This allows me to send all the images generated by getmedia.php to be sent into adaptive-images.php and if it has noadaptive=true as a get parameter then it does not utilize the script so I can get full sized images again.
Here is the modified adaptive-images.php
<?php
//sendErrorImage("there is a problem");
/* PROJECT INFO --------------------------------------------------------------------------------------------------------
Version: 1.5.2
Changelog: http://adaptive-images.com/changelog.txt
Homepage: http://adaptive-images.com
GitHub: https://github.com/MattWilcox/Adaptive-Images
Twitter: #responsiveimg
LEGAL:
Adaptive Images by Matt Wilcox is licensed under a Creative Commons Attribution 3.0 Unported License.
/* CONFIG ----------------------------------------------------------------------------------------------------------- */
/* MODIFICATIONS -------------------------------------------------------------------------------------------------------
Mods by: MLM from VisualPulse.net
Description: Added compatibility for php fed images.
Intended for use with a ambiguous file system that serve the files through a php file
NOTE: These are unofficial modifications I made up to get this wonderful script to work with my file system.
*/
$resolutions = array(1382, 992, 768, 480); // the resolution break-points to use (screen widths, in pixels)
$cache_path = "ai-cache"; // where to store the generated re-sized images. Specify from your document root!
$jpg_quality = 100; // the quality of any generated JPGs on a scale of 0 to 100
$sharpen = TRUE; // Shrinking images can blur details, perform a sharpen on re-scaled images?
$watch_cache = TRUE; // check that the adapted image isn't stale (ensures updated source images are re-cached)
$browser_cache = 60 * 60 * 24 * 7; // How long the BROWSER cache should last (seconds, minutes, hours, days. 7days by default)
/* END CONFIG ----------------------------------------------------------------------------------------------------------
------------------------ Don't edit anything after this line unless you know what you're doing -------------------------
--------------------------------------------------------------------------------------------------------------------- */
/* get all of the required data from the HTTP request */
$document_root = $_SERVER['DOCUMENT_ROOT'];
$requested_uri = parse_url(urldecode($_SERVER['REQUEST_URI']), PHP_URL_PATH);
$requested_uri_more_stuff = parse_url(urldecode($_SERVER['REQUEST_URI']));
$requested_file = basename($requested_uri);
//$source_file = $document_root . $requested_uri;
file_put_contents('array.txt', var_export($requested_uri_more_stuff, TRUE));
if(pathinfo($requested_file, PATHINFO_EXTENSION) == 'php')
$source_file = 'http://' . $_SERVER['HTTP_HOST'] . $requested_uri. ((!empty($requested_uri_more_stuff['query']) ? ('?' . $requested_uri_more_stuff['query']) : '') . (((parse_url($_SERVER['REQUEST_URI'], PHP_URL_QUERY) == NULL) ? '?' : '&') . 'noadaptive=true'));
else
$source_file = $document_root . $requested_uri;
$resolution = FALSE;
//sendErrorImage(pathinfo($requested_file, PATHINFO_EXTENSION));
//sendErrorImage($source_file);
/* Mobile detection
NOTE: only used in the event a cookie isn't available. */
function is_mobile()
{
$userAgent = strtolower($_SERVER['HTTP_USER_AGENT']);
return strpos($userAgent, 'mobile');
}
/* Does the UA string indicate this is a mobile? */
if (!is_mobile()) {
$is_mobile = FALSE;
} else {
$is_mobile = TRUE;
}
// does the $cache_path directory exist already?
if (!is_dir("$document_root/$cache_path")) { // no
if (!mkdir("$document_root/$cache_path", 0755, true)) { // so make it
if (!is_dir("$document_root/$cache_path")) { // check again to protect against race conditions
// uh-oh, failed to make that directory
sendErrorImage("Failed to create cache directory at: $document_root/$cache_path");
}
}
}
/* helper function: Send headers and returns an image. */
function sendImage($filename, $browser_cache)
{
$extension = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if (in_array($extension, array('png', 'gif', 'jpeg'))) {
header("Content-Type: image/" . $extension);
} else {
header("Content-Type: image/jpeg");
}
header("Cache-Control: private, max-age=" . $browser_cache);
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + $browser_cache) . ' GMT');
header('Content-Length: ' . filesize($filename));
readfile($filename);
exit();
}
/* helper function: Create and send an image with an error message. */
function sendErrorImage($message)
{
/* get all of the required data from the HTTP request */
$document_root = $_SERVER['DOCUMENT_ROOT'];
$requested_uri = parse_url(urldecode($_SERVER['REQUEST_URI']), PHP_URL_PATH);
$requested_file = basename($requested_uri);
$source_file = $document_root . $requested_uri;
if (!is_mobile()) {
$is_mobile = "FALSE";
} else {
$is_mobile = "TRUE";
}
$im = ImageCreateTrueColor(800, 300);
$text_color = ImageColorAllocate($im, 233, 14, 91);
$message_color = ImageColorAllocate($im, 91, 112, 233);
ImageString($im, 5, 5, 5, "Adaptive Images encountered a problem:", $text_color);
ImageString($im, 3, 5, 25, $message, $message_color);
ImageString($im, 5, 5, 85, "Potentially useful information:", $text_color);
ImageString($im, 3, 5, 105, "DOCUMENT ROOT IS: $document_root", $text_color);
ImageString($im, 3, 5, 125, "REQUESTED URI WAS: $requested_uri", $text_color);
ImageString($im, 3, 5, 145, "REQUESTED FILE WAS: $requested_file", $text_color);
ImageString($im, 3, 5, 165, "SOURCE FILE IS: $source_file", $text_color);
ImageString($im, 3, 5, 185, "DEVICE IS MOBILE? $is_mobile", $text_color);
//ImageString($im, 3, 5, 205, "Resolution: $resolution", $text_color); // Doesn't work
header("Cache-Control: no-store");
header('Expires: ' . gmdate('D, d M Y H:i:s', time() - 1000) . ' GMT');
header('Content-Type: image/jpeg');
ImageJpeg($im);
ImageDestroy($im);
exit();
}
/* sharpen images function */
function findSharp($intOrig, $intFinal)
{
$intFinal = $intFinal * (750.0 / $intOrig);
$intA = 52;
$intB = -0.27810650887573124;
$intC = .00047337278106508946;
$intRes = $intA + $intB * $intFinal + $intC * $intFinal * $intFinal;
return max(round($intRes), 0);
}
/* refreshes the cached image if it's outdated */
function refreshCache($source_file, $cache_file, $resolution)
{
if (file_exists($cache_file)) {
// not modified
if (filemtime($cache_file) >= filemtime($source_file)) {
return $cache_file;
}
// modified, clear it
unlink($cache_file);
}
return generateImage($source_file, $cache_file, $resolution);
}
/* generates the given cache file for the given source file with the given resolution */
function generateImage($source_file, $cache_file, $resolution)
{
global $sharpen, $jpg_quality;
$extension = strtolower(pathinfo($source_file, PATHINFO_EXTENSION));
// Check the image dimensions
$dimensions = getimagesize($source_file);
$width = $dimensions[0];
$height = $dimensions[1];
//sendErrorImage($width . ' ' . $resolution);
// Do we need to downscale the image?
if ($width <= $resolution) { // no, because the width of the source image is already less than the client width
return $source_file;
}
// We need to resize the source image to the width of the resolution breakpoint we're working with
$ratio = $height / $width;
$new_width = $resolution;
$new_height = ceil($new_width * $ratio);
$dst = ImageCreateTrueColor($new_width, $new_height); // re-sized image
switch ($extension) {
case 'png':
$src = #imagecreatefrompng($source_file); // original image
break;
case 'gif':
$src = #imagecreatefromgif($source_file); // original image
break;
default:
//imagecreatefromstring(file_get_contents($source_file));
$src = #imagecreatefromjpeg($source_file); // original image
ImageInterlace($dst, true); // Enable interlancing (progressive JPG, smaller size file)
break;
}
if ($extension == 'png') {
imagealphablending($dst, false);
imagesavealpha($dst, true);
$transparent = imagecolorallocatealpha($dst, 255, 255, 255, 127);
imagefilledrectangle($dst, 0, 0, $new_width, $new_height, $transparent);
}
ImageCopyResampled($dst, $src, 0, 0, 0, 0, $new_width, $new_height, $width, $height); // do the resize in memory
ImageDestroy($src);
// sharpen the image?
// NOTE: requires PHP compiled with the bundled version of GD (see http://php.net/manual/en/function.imageconvolution.php)
if ($sharpen == TRUE && function_exists('imageconvolution')) {
$intSharpness = findSharp($width, $new_width);
$arrMatrix = array(
array(-1, -2, -1),
array(-2, $intSharpness + 12, -2),
array(-1, -2, -1)
);
imageconvolution($dst, $arrMatrix, $intSharpness, 0);
}
$cache_dir = dirname($cache_file);
//sendErrorImage($cache_dir);
// does the directory exist already?
if (!is_dir($cache_dir)) {
if (!mkdir($cache_dir, 0755, true)) {
// check again if it really doesn't exist to protect against race conditions
if (!is_dir($cache_dir)) {
// uh-oh, failed to make that directory
ImageDestroy($dst);
sendErrorImage("Failed to create cache directory: $cache_dir");
}
}
}
if (!is_writable($cache_dir)) {
sendErrorImage("The cache directory is not writable: $cache_dir");
}
// save the new file in the appropriate path, and send a version to the browser
switch ($extension) {
case 'png':
$gotSaved = ImagePng($dst, $cache_file);
break;
case 'gif':
$gotSaved = ImageGif($dst, $cache_file);
break;
default:
$gotSaved = ImageJpeg($dst, $cache_file, $jpg_quality);
break;
}
ImageDestroy($dst);
if (!$gotSaved && !file_exists($cache_file)) {
sendErrorImage("Failed to create image: $cache_file");
}
return $cache_file;
}
//sendErrorImage($source_file);
// check if the file exists at all
if(pathinfo($requested_file, PATHINFO_EXTENSION) != 'php')
{
if (!file_exists($source_file)) {
//sendErrorImage('404 ' . $source_file);
header("Status: 404 Not Found");
exit();
}
}
else if(pathinfo($requested_file, PATHINFO_EXTENSION) == php)
{
}
/* check that PHP has the GD library available to use for image re-sizing */
if (!extension_loaded('gd')) { // it's not loaded
if (!function_exists('dl') || !dl('gd.so')) { // and we can't load it either
// no GD available, so deliver the image straight up
trigger_error('You must enable the GD extension to make use of Adaptive Images', E_USER_WARNING);
sendImage($source_file, $browser_cache);
}
}
/* Check to see if a valid cookie exists */
if (isset($_COOKIE['resolution'])) {
$cookie_value = $_COOKIE['resolution'];
// does the cookie look valid? [whole number, comma, potential floating number]
if (!preg_match("/^[0-9]+[,]*[0-9\.]+$/", "$cookie_value")) { // no it doesn't look valid
setcookie("resolution", "$cookie_value", time() - 100); // delete the mangled cookie
} else { // the cookie is valid, do stuff with it
$cookie_data = explode(",", $_COOKIE['resolution']);
$client_width = (int)$cookie_data[0]; // the base resolution (CSS pixels)
$total_width = $client_width;
$pixel_density = 1; // set a default, used for non-retina style JS snippet
if (#$cookie_data[1]) { // the device's pixel density factor (physical pixels per CSS pixel)
$pixel_density = $cookie_data[1];
}
rsort($resolutions); // make sure the supplied break-points are in reverse size order
$resolution = $resolutions[0]; // by default use the largest supported break-point
// if pixel density is not 1, then we need to be smart about adapting and fitting into the defined breakpoints
if ($pixel_density != 1) {
$total_width = $client_width * $pixel_density; // required physical pixel width of the image
// the required image width is bigger than any existing value in $resolutions
if ($total_width > $resolutions[0]) {
// firstly, fit the CSS size into a break point ignoring the multiplier
foreach ($resolutions as $break_point) { // filter down
if ($total_width <= $break_point) {
$resolution = $break_point;
}
}
// now apply the multiplier
$resolution = $resolution * $pixel_density;
} // the required image fits into the existing breakpoints in $resolutions
else {
foreach ($resolutions as $break_point) { // filter down
if ($total_width <= $break_point) {
$resolution = $break_point;
}
}
}
} else { // pixel density is 1, just fit it into one of the breakpoints
foreach ($resolutions as $break_point) { // filter down
if ($total_width <= $break_point) {
$resolution = $break_point;
}
}
//sendErrorImage($client_width . " " . $resolution . " " . $pixel_density . " | " . $total_width);
}
}
}
/* No resolution was found (no cookie or invalid cookie) */
if (!$resolution) {
// We send the lowest resolution for mobile-first approach, and highest otherwise
$resolution = $is_mobile ? min($resolutions) : max($resolutions);
}
/* if the requested URL starts with a slash, remove the slash */
if (substr($requested_uri, 0, 1) == "/") {
$requested_uri = substr($requested_uri, 1);
}
/* whew might the cache file be? */
if(pathinfo($requested_file, PATHINFO_EXTENSION) == 'php')
{
$getstring = '';
foreach($_GET as $key => $item)
{
$getstring .= $key . '~' . $item . '^';
}
$fileinfophp = pathinfo($_SERVER['REQUEST_URI']);
//sendErrorImage($getstring);
$cache_file = $document_root . "/$cache_path/$resolution/" . 'php/' . $fileinfophp['filename'] . '%' . $getstring;
}
else
$cache_file = $document_root . "/$cache_path/$resolution/" . $requested_uri;
/* Use the resolution value as a path variable and check to see if an image of the same name exists at that path */
if (file_exists($cache_file)) { // it exists cached at that size
if ($watch_cache) { // if cache watching is enabled, compare cache and source modified dates to ensure the cache isn't stale
$cache_file = refreshCache($source_file, $cache_file, $resolution);
}
sendImage($cache_file, $browser_cache);
}
//sendErrorImage('gen');
/* It exists as a source file, and it doesn't exist cached - lets make one: */
$file = generateImage($source_file, $cache_file, $resolution);
//file_put_contents('image.jpg', $file);
sendImage($file, $browser_cache);
Idea
I have a function that checks to see if a thumbnail exists in cache folder for a particular image. If it does, it returns the path to that thumbnail. If it does not, it goes ahead and generates the thumbnail for the image, saves it in the cache folder and returns the path to it instead.
Problem
Let's say I have 10 images but only 7 of them have their thumbnails in the cache folder. Therefore, the function goes to generation of thumbnails for the rest 3 images. But while it does that, all I see is a blank, white loading page. The idea is to display the thumbnails that are already generated and then generate the ones that do not exist.
Code
$images = array(
"http://i49.tinypic.com/4t9a9w.jpg",
"http://i.imgur.com/p2S1n.jpg",
"http://i49.tinypic.com/l9tow.jpg",
"http://i45.tinypic.com/10di4q1.jpg",
"http://i.imgur.com/PnefW.jpg",
"http://i.imgur.com/EqakI.jpg",
"http://i46.tinypic.com/102tl09.jpg",
"http://i47.tinypic.com/2rnx6ic.jpg",
"http://i50.tinypic.com/2ykc2gn.jpg",
"http://i50.tinypic.com/2eewr3p.jpg"
);
function get_name($source) {
$name = explode("/", $source);
$name = end($name);
return $name;
}
function get_thumbnail($image) {
$image_name = get_name($image);
if(file_exists("cache/{$image_name}")) {
return "cache/{$image_name}";
} else {
list($width, $height) = getimagesize($image);
$thumb = imagecreatefromjpeg($image);
if($width > $height) {
$y = 0;
$x = ($width - $height) / 2;
$smallest_side = $height;
} else {
$x = 0;
$y = ($height - $width) / 2;
$smallest_side = $width;
}
$thumb_size = 200;
$thumb_image = imagecreatetruecolor($thumb_size, $thumb_size);
imagecopyresampled($thumb_image, $thumb, 0, 0, $x, $y, $thumb_size, $thumb_size, $smallest_side, $smallest_side);
imagejpeg($thumb_image, "cache/{$image_name}");
return "cache/{$image_name}";
}
}
foreach($images as $image) {
echo "<img src='" . get_thumbnail($image) . "' />";
}
To elaborate on #DCoder's comment, what you could do is;
If the thumb exists in the cache, return the URL just as you do now. This will make sure that thumbs that are in the cache will load quickly.
If the thumb does not exist in the cache, return an URL similar to /cache/generatethumb.php?http://i49.tinypic.com/4t9a9w.jpg where the script generatethumb.php generates the thumbnail, saves it in the cache and returns the thumbnail. Next time, it will be in the cache and the URL won't go through the PHP script.