Support forum login

©2006-2012 IAAI Software

Contact Us Privacy Policy

Image Caching

Submitted by PriceSpin on Fri, 2006-05-12 11:29.

Is there anyway of caching images so it doesnt pull them off the manufacturers site all the time? I have a server so space isnt an issue.

Cheers..
__________
Price Comparison

Submitted by support on Fri, 2006-05-12 12:24.

Hi Robert,

I described my favorite "generic" file caching technique in this thread on the Magic Parser forum:

http://www.magicparser.com/node/136

It would be reasonably straight forward to adapt this to become an image caching script. It would basically involve writing a script that would become the src attribute for product images, with the original image URL as a parameter. It would then check for the cached version and serve that if it exists, otherwise it would fetch the file from source. This is completely off the top of my head... it should work on Linux, and requires shell access to wget (just like the automation scripts) you're welcome to give it a go:

imageCache.php

<?php
  
function cacheFetch($url,$age)
  {
    
// directory in which to store cached files, must be writable by PHP
    
$cacheDir "cache/";
    
// cache filename constructed from MD5 hash of URL
    
$filename $cacheDir.md5($url);
    
// default to fetch the file
    
$fetch true;
    
// but if the file exists, don't fetch if it is recent enough
    
if (file_exists($filename))
    {
      
$fetch = (filemtime($filename) < (time()-$age));
    }
    
// fetch the file if required
    
if ($fetch)
    {
      
$maxRetry 2;
      do
      {
        
// shell to wget to fetch the file, storing the exit code in $error
        
exec("wget -N -O ".$filename." ".escapeshellarg($url),$output,$error);
        
// update timestamp to now
        
exec("touch ".$filename);
        
// keep trying if wget failed and not reached $maxRetry
      
} while( $error && $maxRetry--);
    }
    
// return the filename only if wget did not fail
    
if (!$error)
    {
      return 
$filename;
    }
    else
    {
      
// as an error occured, delete the empty file so it is retried next time
      
unlink($filename);
      
// return false
      
return false;
    }
  }
  
$src $_GET["src"];
  
$src str_replace(" ","%20",$src);
  
$src cacheFetch($src,604800);
  
$img file_get_contents($src);
  
header("Content-Type: image");
  print 
$img;
?>

The value of 604800 for the $age parameter means 1 week, but you could could use more or less if required - the value is in seconds. To use this on the product page, you would make the following modification to html/product.html

Original:

<img width='180' src='<?php print $mainProduct["image_url"]; ?>' alt='<?php print $mainProduct["name"]; ?>' />

Cached images:

<img width='180' src='/imageCache.php?src=<?php print urlencode($mainProduct["image_url"]); ?>' alt='<?php print $mainProduct["name"]; ?>' />

Similar modifications could of course be made to html/searchresults.php.

This is assuming that your imageCache.php is in the root directory of your website; and if you're using the script as above it would require a writeable subdirectory "cache" at the same location.

Submitted by PriceSpin on Fri, 2006-05-12 18:11.

Thanks David, Ill give this a go and let you know how I get on.

Robert
PriceSpin - Price Comparison
DJHangout - DJ Equipment Comparison

Submitted by PriceSpin on Fri, 2006-05-12 18:25.

David, it appears as tho it aint working:
http://www.djhangout.com/product/Pioneer-CDJ200-Pair.html
but I have in the newly created /cache directory
62674bf11445d25cabfdb64c7318590c but no file extension...

Robert
PriceSpin - Price Comparison
DJHangout - DJ Equipment Comparison

Submitted by support on Fri, 2006-05-12 18:53.

Hi Robert,

By viewing the source on that page, I think the problem is just because you have left the leading "/" character out, so the image is trying to be loaded from the re-written SEO friendly URL.

What this means is that the browser is trying to load:

http://www.djhangout.com/product/imageCache.php?src=http%3A%2F%2Fwww.discostudio.co.uk%2Fimages%2Fshopimages%2F2573.jpg

...notice how it has assumed that imageCache.php is in the same directory as the "product". The URL should actually be:

http://www.djhangout.com/imageCache.php?src=http%3A%2F%2Fwww.discostudio.co.uk%2Fimages%2Fshopimages%2F2573.jpg

I think you have just missed out the "/" in the html/product.html, that's all.

However, on trying to view that URL directly, it would seem that the image data is trying to be processed as PHP. This indicates that require() is not the call to use to include the cached image, and it should instead be the readfile() function.

Simply change:

require($img);

to

readfile($img);

...on the last line of imageCache.php

Submitted by PriceSpin on Fri, 2006-05-12 19:15.

Think thats sorted it... Cheers m8

Robert
PriceSpin - Price Comparison
DJHangout - DJ Equipment Comparison

Submitted by PriceSpin on Fri, 2006-05-12 19:24.

http://www.pricespin.co.uk/search.php?q=xbox&page=4&sort=relevance

Robert
PriceSpin - Price Comparison
DJHangout - DJ Equipment Comparison

Submitted by support on Fri, 2006-05-12 19:41.

Hi Robert,

There's a new-line character crept on to the end of each of the image URLs on that page (do a view-source on the page and you will see %0A on the end of each image URL). Check your source surrounding the urlencode function - it, seems to be urlencoding it; so it's within the function.

I think you should have something like this:

<?php if ($product["image_url"]): ?>
<a href='<?php print $product["productHREF"]; ?>'><img border='0' width='80' src='/imageCache.php?src=<?php print urlencode($product["image_url"]); ?>' alt='<?php print $product["name"]; ?>' /></a>
<?php endif; ?>

Cheers,
David.

Submitted by JasonG on Mon, 2006-09-11 08:24.

Hi,

The above code is exactly what I'm looking for to cache images, however, my host won't enable the 'exec' command.

Is there any other way of doing it?

Many thanks

Jason

Submitted by support on Mon, 2006-09-11 09:14.

Hi Jason,

If your host has disabled exec() it is likely that fopen(url) is not permitted either; however it's worth trying. Instead of:

<?php
  exec
("wget -N -O ".$filename." \"".$url."\"",$output,$error);
?>

Use:

<?php
  $fp_src 
fopen($url,"r");
  
$fp_dst fopen($filename,"w");
  while(!
feof($fp_src))
  {
    
fwrite($fp_dst,fread($fp_src,512));
  }
  
fclose($fp_dst);
  
fclose($fp_src);
  
$error 0;
?>

You don't need the exec() call to "touch" doing it this way...

Cheers,
David.

Submitted by JasonG on Mon, 2006-09-11 10:01.

Hi David,

Many thanks for the quick response as always.

I'm just trying to get it working on my local web server first before uploading it to my live site and trying it.

On my home page I have 6 images to display (featured items). 4 of the images display and are cached perfectly but the other 2 appear to be cached but not displayed. The only obvious difference I can see for the non displayed images is the length of the image url. Is there a maximum length of url and if so can it be increased?

Many thanks

Jason

Submitted by support on Mon, 2006-09-11 10:27.

Hi Jason,

Are the images that don't cache from the same host, or just happen to have longer URLs.

What happens if you browse to the URL of the cache file directly in the browser, for example:

http://www.yoursite.com/cache/xxxxxxxxxxxxxxxxxxxxxxxxxxx

Cheers,
David.

Submitted by JasonG on Mon, 2006-09-11 10:37.

Hi David,

The 2 images that don't display (with the long URL) are from the same host although other images from the same host display ok.

If I browse directly to the URL of the cached file the images display fine which indicates that the URL is not the problem. Hmmmm now I'm stumped??

Regards

Jason

Submitted by support on Mon, 2006-09-11 11:27.

Hi Jason,

I've come across wierd stuff like this before when trying to do image caching. Can you drop me the URL of the page that is showing the "broken" images and i'll take a look (email me if you don't want to post in the forum)....

Cheers,
David.

Submitted by support on Mon, 2006-09-11 11:37.

Another thought, Jason....

Did you see the post above about using readfile() instead of require() as in the original script...

At the bottom of your imageCache.php script, you might need to change:

require($img);

to

readfile($img);

Cheers,
David.

Submitted by JasonG on Mon, 2006-09-11 12:08.

Hi David,

Excellent ... it's working like a dream.

Cheers David for all your help.

Regards

Jason

Submitted by webie on Sat, 2007-04-21 15:46.

Hi David

This cache image is perfect for me to download images from merchant sites I have very limited PHP code experience How would I turn your code into ssh enabled script to run via cron to download merchant images all at once reading image url from the database and storing serverside. Is it also possible to control with config to display cached images or not also I would like to store images for 31 days or more do you have information or link on cache times.

Many Thanks
Darren

Submitted by support on Sat, 2007-04-21 16:53.

Hi Darren,

The cache time is configurable in this caching script with the following line:

  $img = cacheFetch($src,604800);

In this case, 604800 is the cache duration in seconds; so you can change this to anything you want. I would advise against attempting to preload your cache from a merchants server as this could easily result in your server being blocked from the merchants image server - and if using the cache script this would then mean that no images would be displayed for that merchant...!

If you really want to do this let me know and i'll work through a script to read the Price Tapestry database and request each image through the cache. Don't forget to consider disk space and bandwidth implications if doing this...

Cheers,
David.

Submitted by webie on Fri, 2007-04-27 02:18.

Hi David,

Just wondering if you had anymore thought on this image cache as i having problems with missing images from merchants i have tried couple fixes like web safe images one was javascript but this was stopping images downloading on about 10% of merchants and the same for html fix wondering if you had any fixes if image not found to load default image from my server.

Many Thanks
Darren

Submitted by support on Fri, 2007-04-27 08:08.

Hi Darren,

A couple of users have tried experiments to do something about this (i.e. using JavaScript); but nothing turned out to be satisfactory. The problem of course is that you have no control over whether or not the image has loaded - you can only see that from viewing the page.

What it is always worth doing in this instance is contacting the merchant or their account manager at the affiliate network and raise the issue with them - it is a quality control matter after all and the network or merchant should be interested in resolving the problem from there end. I have heard of a number of success stories where users have been able to get merchants to make corrections or changes to make life easier for feed users...

Cheers,
David.

Submitted by webie on Tue, 2007-05-01 21:36.

Hi David

Just quick question on this image cache downloader can the catched file be store in database instead of a directory or would that not be good I going to post a project on rac to have this coded so i can store images serverside ask affiliates network but did not get good answer.

Good luck with your move don't strain your back now we need you fighting fit.

all the best
Darren

Submitted by support on Thu, 2007-05-03 09:01.

Hello Darren,

In theory it could be done with a database, but because of the size of the objects and the number of times they will be requested I would not recommend it...

Cheers,
David,

Submitted by webie on Sat, 2007-05-05 13:40.

Hi David,

I know you are busy but had a go of coding this image cache downloader this is what i got so far but i got some warnings and as i am still learning i have search the web but with now success also is there a way of telling the code that if image url is not found to cache serverside image to replace the one which was not found.

Errors:
Missing argument 2 for cacheFetch() line 30
Unsupported scheme

Here is my first attempt of code in PHP

<?php
set_time_limit(0);
error_reporting(E_ALL ^ E_NOTICE);
require("config.php");
$link = mysql_connect( $config_databaseServer, $config_databaseUsername, $config_databasePassword );
if ( ! $link ) die( "Couldn't connect to MySQL" );
mysql_select_db( $config_databaseName, $link )
or die ( "Couldn't open $config_databaseName: ".mysql_error() );
$result = mysql_query( "SELECT `products`.`image_url` FROM products LIMIT 5" );
$num_rows = mysql_num_rows( $result );
// print "<p>There are currently $num_rows rows in the table</P>";
while ( $a_row = mysql_fetch_row( $result ) )
// {
/// print "<tr>\n";
foreach ( $a_row as $url )
// print "\t<td>$url<br><hr size=\"1\" /></td>\n";
// print "</tr>\n";
// print "</table>\n";
// }
// mysql_close( $link );
if ($filename = cacheFetch("$url"))
       {
       echo 'Temporarily unavailable, please try later.';
       }
function cacheFetch($url,$age)
  {
    // directory in which to store cached files, must be writable by PHP
    $cacheDir = "cache/";
    // cache filename constructed from MD5 hash of URL
    $filename = $cacheDir.md5($url);
    // default to fetch the file
    $fetch = true;
    // but if the file exists, don't fetch if it is recent enough
    if (file_exists($filename))
    {
      $fetch = (filemtime($filename) < (time()-$age));
    }
    // fetch the file if required
    if ($fetch)
    {
      $maxRetry = 2;
      do
      {
        // shell to wget to fetch the file, storing the exit code in $error
        exec("wget -N -O ".$filename." \"".$url."\"",$output,$error);
        // update timestamp to now
        exec("touch ".$filename);
        // keep trying if wget failed and not reached $maxRetry
      } while( $error && $maxRetry--);
    }
    // return the filename only if wget did not fail
    if (!$error)
    {
      return $filename;
    }
    else
    {
      // as an error occured, delete the empty file so it is retried next time
      unlink($filename);
      // return false
      return false;
    }
  }
  $src = $_GET["src"];
  $img = cacheFetch($src,604800);
  readfile($img);
?>

Submitted by support on Sun, 2007-05-06 09:36.

Hi,

As I understand it, you are trying to write a script to pre-load your cache. If this is the case, I think the last 3 lines of your current script are redundant, presumably copied from the example code above. I've removed these, and also made a small structural change to the loop section of your script.

The error that you were getting was because of the missing $age parameter in the call to cacheFetch(). I've added this parameter, and changed the way the test is done.

I've also modified your code to make it use a server image if the image is not available, using copy() to copy your default image as the cache filename. With this code, you need to make your default image as "missing.jpg" in the same directory as this script (your Price Tapestry install directory).

<?php
  set_time_limit
(0);
  
error_reporting(E_ALL E_NOTICE);
  require(
"config.php");
  
$link mysql_connect$config_databaseServer$config_databaseUsername$config_databasePassword );
  if ( ! 
$link ) die( "Couldn't connect to MySQL" );
  
mysql_select_db$config_databaseName$link )
   or die ( 
"Couldn't open  $config_databaseName: ".mysql_error() );
  
$result mysql_query"SELECT `products`.`image_url` FROM products LIMIT 5" );
  
$num_rows mysql_num_rows$result );
  while ( 
$a_row mysql_fetch_row$result ) )
  {
    foreach ( 
$a_row as $url )
    {
      
$filename cacheFetch($url,604800);  // needed a timeout value here
      
if (!$filename)
      {
        echo 
'Temporarily unavailable, please try later.';
      }
    }
  }
  function 
cacheFetch($url,$age)
  {
    
// directory in which to store cached files, must be writable by PHP
    
$cacheDir "cache/";
    
// cache filename constructed from MD5 hash of URL
    
$filename $cacheDir.md5($url);
    
// default to fetch the file
    
$fetch true;
    
// but if the file exists, don't fetch if it is recent enough
    
if (file_exists($filename))
    {
      
$fetch = (filemtime($filename) < (time()-$age));
    }
    
// fetch the file if required
    
if ($fetch)
    {
      
$maxRetry 2;
      do
      {
        
// shell to wget to fetch the file, storing the exit code in $error
        
exec("wget -N -O ".$filename." \"".$url."\"",$output,$error);
        
// update timestamp to now
        
exec("touch ".$filename);
        
// keep trying if wget failed and not reached $maxRetry
      
} while( $error && $maxRetry--);
    }
    
// return the filename only if wget did not fail
    
if (!$error)
    {
      return 
$filename;
    }
    else
    {
      
// as an error occured, copy the default image as the image
      
unlink($filename);
      
copy("missing.jpg",$filename);
      return 
$filename;
    }
  }
?>

Remember that you will need to use this in addition to the original cache script above, so you might want to call this file imageCachePreLoad.php and use imageCache.php unmodified as described above (and presumably you already have working).

Hope this helps,
Cheers,
David.

Submitted by webie on Mon, 2007-05-07 18:14.

Hi David;

Just big thank you for your time and experience once again thank you

all the best
Darren

Submitted by webie on Wed, 2007-12-12 15:36.

Hi Dave,

A while ago you coded the preload cacge images for me which works well is there away of cleaning up cached images what i mean is if i delete merchant from my index is there away of deleting the cached images also.

cheers

Darren

Submitted by support on Wed, 2007-12-12 15:39.

Hi Darren,

As it stands, there is no way to identify which images belong to which merchant. However, I would actually recommend a scheduled (cron) deletion of all cached images. This will ensure that any changes in images are picked up; and keep the disk space used in-check...

Cheers,
David.

Submitted by coyote on Fri, 2008-01-25 19:15.

Thanks for this mod

it works perfectly for me

i'm currently working with a server that doesn't accept exec so that i picked the code David gives above

I wonder if exec is better, i mean is your replacement code does exactly the same thing ? (in terms of speed, etc)

I am also trying to rewrite :

http://www.mywebsite/shopping/imagecache.php?src=

<?php
 
print urlencode($mainProduct["image_url"]); 
?>

by something which would give a better url like :

http://www.mywebsite/shopping/imagecache/imgxxxx.jpg for instance

is it possible ?

Thanks
Coyote

Submitted by PaulSmith on Sat, 2008-01-26 13:43.

Hi David

I've tried to install the image cache feature at the start of this thread, however i'm having a few issues getting it to work.

I've created the imagecache.php file, created cache directory and made changes to product.php in the html folder.

The images arn't showing, i think the reason is that the image location appears to have filled with some random charchters, for example:

http://www.mydomain.com/imageCache.php?src=http%3A%2F%2Fwww.merchant.com%2Fpic%2Fp1732h.jpg

The merchants URL should be:

http://www.merchant.com/pic/p1732h.jpg

Prior to trying to implement the cache the images show fine with the correct urls, and still do if i reverse the changes back so they are hotlinking from the merchant as before.

Any idea why this is happening and how I fix it?

Thanks for your help

Paul

Submitted by support on Mon, 2008-01-28 09:45.

Hello Paul,

The most likely reason is that PHP is not permitted to exec() to wget on your server. This is discussed above - could you have a read through the follow-up's to the original solution and see if anything there applies to you - there is an alternative version you might be able to use if exec() is disabled on your server...

Cheers,
David.

Submitted by coyote on Wed, 2008-03-12 16:13.

Hello

I used this script with success on one of my hostings

but on a new dedicated server on which i prefer not to pass safe mode on off it doesnt work, either by fopen or exec method

i wonder if there is another method ?

Thank you for your answer

Coyote

Submitted by support on Wed, 2008-03-12 16:31.

Hello Coyote,

As the remote file must be retrieved for this technique to work you will need to use one of these method, which will mean running at least this site out of safe mode.

As you have control over all sites (I assume that it's your dedicated server) you should be able to allow_url_fopen on just this configuration; without enabling safe mode globally...

Cheers,
David.

Submitted by coyote on Wed, 2008-03-12 16:44.

Yes you're right i can only allow url fopen

but i heard allowing url fopen could create some dangerous possibilities for hackers

is it really dangerous ?

Submitted by support on Wed, 2008-03-12 16:47.

As long as you restrict it to the one site; and you have taken all usual anti-hacker precautions you should be safe. Remember that in order to use it hacker would have to have managed to access your site anyway, which is a far greater security risk than anything they are likely to do with fopen()...

Cheers,
David.

Submitted by coyote on Thu, 2008-03-13 15:34.

hi David

I made changes and checked my php info

allo_url_fopen is on

it doesnt work neither ... images dont appear and there is nothing in /cache/ directory

where could be the problem

i noticed allo_url_include was off, should i change it ?

Submitted by support on Thu, 2008-03-13 15:38.

Hi,

The main requirement (if fopen() with URLs working) is that /cache/ is WRITABLE by PHP.

The easiest thing to do is to make it world writable. If you are logged in, you can do:

chmod a+w cache

(from the directory above it)

Alternatively, you can normally do it with your FTP program. If you right-click on the cache folder in the remote window, then look for Permissions, or perhaps "Properties..." then Permissions; and look for some way to set write access on the folder for Everybody/World...

Cheers,
David.

Submitted by mally on Fri, 2008-07-25 21:34.

Hello David

Can you check this for me please.

I've used the code above and the image is showing, I've check my cache folder and images are in there.

Do the product use the saved image or not as when I refresh the imagelink is the same as the last.

Can ou have a look at the image on Flight International Magazine

Cheers

MAlly

Submitted by support on Sat, 2008-07-26 09:45.

Hi Mal,

The page looks OK - if the problem is the image not refreshing in line with a new image from the merchant the thing to look at the cache age value in use. If this is set to, for example, 7 days, then it could be up to that time before a new image appears.

I think there is also only 1 existing for that merchant and that issue as there is only 1 image showing in the previous issues list at the bottom of the page - so it may be that they just don't update the image that often...

Cheers,
David.

Submitted by mally on Tue, 2008-07-29 21:04.

hello David

To be honest I'm not sure exactly what this mod does.

Can you explain please.

I would like to be able to add all the images to my server so basically the first time ever a product is looked at, the image is copied onto my server. The next time the product is viewed by whoever, the image is shown from my server.

Is that what this tool does?

Thanks

Mally

Submitted by support on Wed, 2008-07-30 07:32.

Hi Mal,

That's correct - but I think we took it a step further on your site by storing past images in order to display previous titles...

If there's a page that isn't being displayed correctly in this way drop me an email and i'll take a look for you...

Cheers,
David.

Submitted by mally on Wed, 2008-07-30 19:07.

Hi David, agh I understand now, yep, I've added the above code also, cool

My only question now is how do a link to a cached image on another site for example adding it to a forum?

Cheers

Mally

Submitted by support on Thu, 2008-07-31 08:06.

Hi Mal,

You can link directly to the image caching script, with the required image URL as a parameter, exactly as the image SRC appears on your main site, so for example:

<img src='http://www.example.com/imageCache.php?src=http://www.example.org/path/to/image.jpg' />

(where example.com is your site and example.org is the site with the image you want to cache)

Cheers,
David.

Submitted by teezone on Sun, 2008-09-21 02:22.

Hi David, this isn't quite working for me..

I replaced wget with:

 $fp_src = fopen($url,"r");
  $fp_dst = fopen($filename,"w");
  while(!feof($fp_src))
  {
    fwrite($fp_dst,fread($fp_src,512));
  }
  fclose($fp_dst);
  fclose($fp_src);
  $error = 0;

..and also used readfile($img);

Is there anything else I can try..? Where it's supposed to be a jpg file-type, it's just lots of text. I also checked the mapping to /imageCache.php, and it's fine.

Image link (example):
http://www.mydomain.com/imageCache.php?src=http%3A%2F%2Fwww.merchant.com%2Fimages%2Fproducts%2F17707%2F17707_in_l.jpg

This just displays as text in browser.

Thanks!

Submitted by support on Sun, 2008-09-21 07:37.

Hi,

Firstly, check that there is no white space at the beginning or end of your script (outside of the PHP tags), as this would break the output format and invalidate the image.

If that's not the problem, if you could email me your imageCache.php i'll check out on my test server for you....

Cheers,
David.

Submitted by teezone on Sun, 2008-09-21 17:48.

Hi David, no white space found, so I have sent the file..

Thanks a mil!
T.

Submitted by cq on Tue, 2008-09-23 11:10.

Hello David,

Created imageCache.php load to server in the main directory. Created folder cache chmod to 777.

How can i change the below code as it has the javascripts in it.

  <td width='250' cellpadding='5' style='border:1px solid;border-color:#6699ff;color:#000000;' >
        <?php if ($mainProduct["image_url"]): ?>
          <img width='200' height='200' onload='JavaScript:if(this.width > 250) this.width=250;'src='<?php print $mainProduct["image_url"]; ?>' alt='<?php print $mainProduct["name"]; ?>' />
<?php endif; ?>
  </td>

As for the html/searchresults.php, i changed the line it works but not the html/product.php

thanks

Submitted by support on Tue, 2008-09-23 11:13.

Hi,

That section would be as follows with imageCache.php:

  <td width='250' cellpadding='5' style='border:1px solid;border-color:#6699ff;color:#000000;' >
        <?php if ($mainProduct["image_url"]): ?>
          <img width='200' height='200' onload='JavaScript:if(this.width > 250) this.width=250;' src='/imageCache.php?src=<?php print urlencode($mainProduct["image_url"]); ?>' alt='<?php print $mainProduct["name"]; ?>' />
<?php endif; ?>
  </td>

Cheers,
David.

Submitted by cq on Tue, 2008-09-23 11:38.

Hi David,

Everything works fine.

thank you so much

cheers
jack

Submitted by atman on Mon, 2008-10-06 17:58.

cq and david.

Submitted by atman on Mon, 2008-10-13 21:29.

david,

I got this script above working. thanks.

is it possible to cache the images but not rewrite the files using MD5 hash?

if possible, the images could be saved as

to a folder like

/savedimages

with a url structure like

http_ystoretools_com_images_100x500_cetheprch15c_jpg.jpg

so that we can link to it directly on our site.

thanks in advance.

Submitted by support on Tue, 2008-10-14 07:51.

Hi atman,

Sure you could change the naming scheme to do something like this. To have a go at this, try replacing the following code:

    // cache filename constructed from MD5 hash of URL
    $filename = $cacheDir.md5($url);

...with this version:

    // cache filename constructed from URL replacing :, ., / and SPACE with _
    $temp = $url;
    $temp = str_replace(":"," ",$temp);
    $temp = str_replace("."," ",$temp);
    $temp = str_replace("/"," ",$temp);
    $temp = str_replace(" ","_",$temp);
    $filename = $cacheDir.$temp;

Cheers,
David.

Submitted by atman on Thu, 2008-10-16 03:24.

hi david.

Submitted by Andrewc on Sat, 2009-06-06 15:27.

Taking the image caching to the next level..

Any chance of creating 2 thumbnails of the downloaded image?

Submitted by support on Sat, 2009-06-06 17:35.

Hi Andrew,

Have followed up to your email...

Cheers,
David.

Submitted by Andrewc on Sun, 2009-06-07 15:28.

David:
"There was no attachment, but some users have found it works better if
you include an image content-type header at the top of the script, in
other words, add the following code immediate after the openning PHP
tag in your imageCache.php script:"

header("Content-Type: image");

Works fine now! thanks

Submitted by mally on Sat, 2009-11-21 16:03.

Hi David

ref this mod, is it possible to set the size of an image, (I mean height rather than space) when the image is veiwed. I can't do this on the site where I'm showing it. I'm wondering if I can set a size when the image is first saved to my server, thanks Mally

Submitted by support on Sun, 2009-11-22 13:44.

Hi Mally,

Yes, it should be possible to use PHP's GD library imaagecopyresized() function to do this. If you could email me your existing cache function and let me know the details of how you'd like the resizing to work i'll have a go at it for you...

Cheers,
David.

Submitted by wdekreij on Wed, 2010-02-10 15:41.

Hmm.. Trying to get it to work, but so far I'm only getting empty (0kb) files in my cache-folder (and obviously only a white screen while trying to load the script).

I have chmodded the cache dir to 777. I have tried both exec, as well as the given fopen-script. Require or Readfile on the end of the script also doesn't change a thing.

Any suggestions?

Submitted by support on Wed, 2010-02-10 16:30.

Hi Wilco,

Could you try commenting out the following line:

        exec("touch ".$filename);

..as the touch command generates a file if it does not already exist; and that will enable you to test the fetch methods. I think the fact that touch works means that it isn't a permissions problem; so the other possibility is that your server is prevented from making outgoing HTTP connections.

Could you test this with the following script...

<?php
  $url 
"http://newsrss.bbc.co.uk/rss/newsonline_uk_edition/front_page/rss.xml";
  
$fp fopen($url,"r");
  if (!
$fp)
  {
    print 
"fopen() succeeded";
  }
  else
  {
    print 
"fopen() failed";
  }
?>

Assuming that fails, first check that fopen() by URL is enabled - you can do this with a simple phpinfo() script...

<?php
  phpinfo
();
?>

...and then look for the "allow_url_fopen" setting.

Now, if that is "On" but the above fopen() test is failing, that would imply the server being firewalled, so the best thing to do would be a call to your host and see if it would be possible to enable outbound HTTP access for your account...

If not, let me know, and perhaps if you want to email me your cache script i'll check it out and have a look at what debug code to add...

Cheers,
David.

Submitted by wdekreij on Wed, 2010-02-10 16:33.

Thanks Morris!

The fopen-test returns a fail, but phpinfo shows that fopen is enabled (allow_url_fopen = on) on the server. I'll get in touch with the hoster!

Submitted by wdekreij on Fri, 2010-02-12 14:29.

I just got word from my hoster.

He told me he didn't know why the above script (BBC XML test) wasn't working. He tested it on 3 other servers, and they all gave the same result ("fopen () failed').

Any thoughts?

Submitted by support on Fri, 2010-02-12 15:35.

Hi Wilco,

It's worth trying a different URL - have a go with this for example:

<?php
  $url = "http://www.example.com/";
  $fp = fopen($url,"r");
  if (!$fp)
  {
    print "fopen() succeeded";
  }
  else
  {
    print "fopen() failed";
  }
?>

Cheers,
David.

Submitted by GemViper on Wed, 2010-05-05 09:17.

I have this working well, it protects my site from downtime on the affiliate servers end. A+

To make this simple script perfect it would need to have "imagecopyresampled" applied so that images are scaled proportionally as well. I'm not sure if reducing total filesize would be possible at the same time without slowing down the page load noticably?

Submitted by support on Wed, 2010-05-05 10:07.

Hi Gem,

The image resize code only needs to apply the first time an image is requested, and it is a reasonably quick process so worth giving a go.

With an image having been received to a local file held in the variable $filename, the following code will resize the image and replace $filename with the resized version:

        $src = file_get_contents($filename);
        $oldImage = imagecreatefromstring($src);
        $oldX = imagesx($oldImage);
        $oldY = imagesy($oldImage);
        if ($oldX && $oldY)
        {
          $newX = 150;
          $xFactor = ($newX / $oldX);
          $newY = intval($oldY * $xFactor);
          $newImage = imagecreatetruecolor($newX,$newY);
          imagecopyresized($newImage, $oldImage, 0,0,0,0, $newX, $newY, $oldX, $oldY);
          imagejpeg($newImage,$filename);
        }

(specify the new image width you want as the value of $newX (line 7 of the above snippet). $newY is then calculated in proportion, otherwise the image would be distorted...)

Cheers,
David.

Submitted by GemViper on Wed, 2010-05-05 20:21.

Bam, there it is!

Would the image rewrite rule for this be... ?
RewriteRule ^images/(.*)$ http://www.example.com/imageCache.php?src=$1 [R,L]

Right now if someone attempts to "view image" the image returns correctly with the "imageCache.php?src=" variable and if they try to "save image" the browser attempts to same "imageCache.php". A cleaner url and actual image save would be better but are those possible with this script as it is now?

Submitted by support on Thu, 2010-05-06 08:13.

Hi Gem,

Bear in mind that Price Tapestry already includes an actual /images/ folder, so it may be safer to use something like /cimages/. Also, since you want an internal redirection I think only the [L] flag would be required. Have a go with:

RewriteRule ^cimages/(.*)$ imageCache.php?src=$1 [L]

Cheers,
David.

Submitted by GemViper on Thu, 2010-05-06 09:38.

I tested an image from the correct folder as well as one from an affiliate site and am able to rewrite those but for whatever reason I cannot seem to rewrite images that have gone though this script. Is it possible that rewrite is failing because of the script itself ?

Submitted by support on Thu, 2010-05-06 09:45.

Hi Gem,

I can't see an obvious reason why it shouldn't work; but what I would do first is put at the top of your imageCache.php just:

<?php
  
print $_GET["url"];exit();
?>

Then browse to an image URL via the /cimages/ rewrite, and double check that the URL looks correct - that's the first test I would do...

Cheers,
David.

Submitted by Keeop on Thu, 2010-05-06 10:16.

On a different tack here, but does anyone have problems getting image caching working with images from certain addresses? I guess this comes from using WGET to pull the image from the server, which doesn't seem to work, as opposed to accessing the image url in a browser, which is fine.

Any ideas on ways around this or if it's just me having this particular problem?!

Cheers.
Keeop

Submitted by support on Thu, 2010-05-06 11:41.

Hi Keeop,

It is possible to specify the user-agent that WGET should use in the command line using the --user-agent='' option, for example:

--user-agent='Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)'

Another option is to include --referer='http://www.example.com/something.html' as some servers may be configured to return images only if a referring URL is set.

Hope this helps!

Cheers,
David.

Submitted by Keeop on Thu, 2010-05-06 15:09.

Nice on David - the 'User-Agent' parameter has fixed it!

Cheers.
Keeop

Submitted by GemViper on Wed, 2010-05-12 00:07.

I'm having some problems with certain images that the affiliate program has been "helpful" with by adding things like "?$size=medium". It's breaking the url path.

Is there a way to filter "?$size" from the cached image, the image loads fine without the parameter and I'm specifying size anyway.

Thanks in advance.

Submitted by GemViper on Wed, 2010-05-12 07:23.

A little bug with the resizing, I've been trying to fix this one but can't seem to get it right.

When I set the "$newX = 150;" in the resizing mod you mentioned a few messages up David it works great but it doesn't stop images from breaking the page layout.

Example $newX = 150; limits an image width to 150 pixels but if the image was 1000 pixels in height... (ie: fishing rods for example) it remains extremely tall.

Is there a way of specifying $newX AND $newY so that it will resize an image properly without exceeding height or width?

Submitted by GemViper on Wed, 2010-05-12 08:28.

RESOLVED

Hi David, I posted asking for help with max width and height but in playing with the code I've solved the problem myself. Here's what I came up with.

~ Set max width ($width) and height ($height) before $fetch = true;
~ replace existing code between the lines "exec(..." with

list($width_orig, $height_orig) = getimagesize($filename);
$ratio_orig = $width_orig/$height_orig;
if ($width/$height > $ratio_orig) {
$width = $height*$ratio_orig;
} else {
$height = $width/$ratio_orig;
}
$newImage = imagecreatetruecolor($width, $height);
$image = imagecreatefromjpeg($filename);
imagecopyresampled($newImage, $image, 0, 0, 0, 0, $width, $height, $width_orig, $height_orig);

~ return imagejpeg($newImage, null, 100); INSTEAD OF return $filename;

Note - You can set the filesize (bytes) to a lower amount by changing the return imagejpeg of 100 to say 75(%) for added bandwidth savings.

Submitted by Keeop on Sat, 2010-06-19 14:19.

Hi,

Any one have a ready-made script for deleting old and unused cached image files? I guess something to delete any images over x days old would be the most basic way of doing it but then this would remove live cached image files too. Would there be a way to alter the main image caching script so the image file date gets updated each time the image is viewed - that way live images will remain on the server. Obviously, to update the file each time would defeat the object of the cache so if just one byte or something could be written to update the date attribute, that would be better.

Any one any ideas, please?

Cheers.
Keeop

Submitted by support on Sun, 2010-06-20 05:04.

Hi Keeop,

Sure - you could update the timestamp each time the image is requested rather than just the first time. In your imageCache.php, you should have the following line within the cacheFetch function:

        exec("touch ".$filename);

Delete / comment out that line from there, and then towards the end of your imageCache.php, look for the following code:

  $src = cacheFetch($src,604800);
  $img = file_get_contents($src);

...and REPLACE that with:

  $src = cacheFetch($src,604800);
  $img = file_get_contents($src);
  exec("touch ".$src);

Hope this helps!

Cheers,
David.

Submitted by Keeop on Thu, 2010-06-24 11:45.

Thanks David, will give that a go.

On a similar, image-based point, I have moved servers and now any image files without an extension are not loading. Do you know how to fix this? Is this something within php.ini that needs addressing?

Cheers.
Keeop

Submitted by support on Thu, 2010-06-24 13:51.

Hi Chris,

It sounds like your imageCache.php may not be sending the "Content-Type: image" header - check your version and make sure that you have this line:

  header("Content-Type: image");

If not, add that line close to the end of the script, but BEFORE it outputs the actual image data - that should be all it is. If you're not sure of course, email me your imageCache.php and I'll check it out for you...

Cheers,
David.

Submitted by Keeop on Thu, 2010-06-24 13:58.

Hi David,

As a follow up and maybe taking this slightly off topic, I've found a few issues since moving servers, all related to loading up image files from folders, so it seems. I have a folder called 'logos', like may users, with the merchants' logo images. Everything was fine using the following code on my old server:

if (file_exists("logos/".$product["merchant"]))
      {
        print "<a href='".$config_baseHREF.".tapestry_hyphenate($product["merchant"])."/'><img style='border: 1px solid silver;' src='".$config_baseHREF."logos/".$product["merchant"]."' alt='View Profile and Details of ".$product["merchant"]."' title='View Profile and Details of ".$product["merchant"]."'></a>";
      }

However, on the new server, I have found that without a file extension, when trying to open image files the page results in a 404. Also, 'file_exists' no longer seems to work unless I add $_SERVER{'DOCUMENT_ROOT'} before the rest of the path.

Any ideas on how to fix this globally? Otherwise, I've got a shed load of sites and files to trawl through to make these changes to get the images loading up correctly. The hosting platform has changed thus:

O/S - Windows 2000 -> Windows 2003 (hence IIS 5 -> IIS 6)
PHP - v5.2.6.6 -> v5.2.13.13 (plus new php.ini configuration)

Thanks.
Keeop

Submitted by Keeop on Thu, 2010-06-24 14:00.

Hi David,

Just posted again - this seems to be affecting non-cached image files - the cached ones seem to be OK. You might want to move these posts as they're not as related to caching as I thought they were!

Cheers.
Keeop

Submitted by Keeop on Thu, 2010-06-24 21:02.

Hi David,

Fixed it. IIS 6 by default only opens files with known MIME types/extensions so image files with no extension are not being opened - you were spot on with your MIME type theory. Anyway, all seems to be working again for me now.

Cheers.
Keeop

Submitted by Keeop on Fri, 2010-06-25 09:57.

Hi David,

Me again! OK, I'm slowly fixing my migration errors but have another related to this topic! Before, when using 'wget', I was using the extra parameter, as sorted by you, of: --user-agent='Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1)' This worked brilliantly but now I can no longer run 'exec' so am having to use the alternative 'fopen' commands. So, my question is, is there a similar parameter I can use for 'fopen' as I now can't access those images again, presumably for the same reason?

Cheers.
Keeop

Submitted by support on Fri, 2010-06-25 11:06.

Hi,

Have a go with

ini_set("user_agent","compatible; MSIE 6.0; Windows NT 5.1; SV1");

...just before your fopen() code...

Hope this helps!

Cheers,
David.

Submitted by Keeop on Fri, 2010-06-25 11:57.

Hi David,

Many thanks for that. However, it doesn't seem to work all of the time which is a tad strange. It works on some images but not others, from the same source web site. The imagecache.php file is creating 0 byte sized files for the ones that aren't working rather than deleting those files and using a standard 'noimage' file which the script is set to do. On page refresh, the 'noimage' file is then used and some of the 0 byte files are then deleted, which is all very weird! Any ideas as to what may be happening here? For web sites that don't require the 'user-agent', everything is working as it should by the looks of things.

Cheers.
Keeop

Submitted by support on Fri, 2010-06-25 13:03.

Hi Keeop,

Could I suggest having a go with the CURL equivalent as an alternative to fopen...

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0 );
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_USERAGENT, "compatible; MSIE 6.0; Windows NT 5.1; SV1");
$src = curl_exec($ch);

You can also use CURL in the same way as shelling out to exec by getting it to write to a file...

$ch = curl_init($url);
curl_setopt($ch, CURLOPT_HEADER, 0 );
curl_setopt($ch, CURLOPT_FILE, $filename);
curl_setopt($ch, CURLOPT_USERAGENT, "compatible; MSIE 6.0; Windows NT 5.1; SV1");
curl_exec($ch);

(in both cases image URL in $url and in the second example the cache file name is in $filename)

Hope this helps!

Cheers,
David.

Submitted by Keeop on Fri, 2010-06-25 16:58.

Hi David,

Thanks for these. The first method didn't work for me but the second did, although it was still having problems with some images and not seeming to use the cache folder. I don't really understand what's causing this. I think I need to find a way of getting the old 'exec' to work again as this method was fine.

Cheers.
Keeop

Submitted by don_load on Wed, 2010-07-28 18:45.

just noticed a perculiar issue. When I view a product for the first time the image is cached and displayed on screen. If I hit refresh or revisit the page it no longer displays an image, just the alt text. Checking the cache folder shows the images to have been saved without a file extension. Is this right?

Seems to me like its trying to use the stored image and having no luck. Do you know what might cause this?

regards,
Jay

Submitted by support on Wed, 2010-07-28 19:45.

Hi Jay,

It may be related to the issue that is resolved with the content-type: image header described here - if not, perhaps if you could email me your image cache script and URL demonstrating the problem I'll check it out for you...

Cheers,
David.

Submitted by wdekreij on Fri, 2010-12-24 12:28.

I'll start of with a modification of this script of which other people might benefit, whereafter I have a question as well. I've made it so that I can load images by an URL as /images/3843_productname.jpg, by adding the following line to .htaccess:
RewriteRule ^images/(.*)_(.*).jpg$ imageCacheID.php?id=$1 [L]
In imageCacheID.php I look the image-URL up before starting the cache process.

I've also added code to make the image smaller (by giving it a fixed hight), and I have a question regarding this part: with the following imageCacheID.php, the first time the image is loaded shows up in full size. It's only from the 2nd time on, that it's giving a smaller image. Any thoughts how to fix this?

ImageCacheID.php:

<?php
  
require("includes/common.php");
    
// Get image based on product-ID
    
$id $_GET['id'];
    
$sql "SELECT * FROM `".$config_databaseTablePrefix."products` WHERE id = '".database_safe($id)."'";
    
$numRows database_querySelect($sql,$rows);
    
$product["products"] = $rows;
    
$mainProduct $product["products"][0];
  function 
cacheFetch($url,$age)
  {
    
// directory in which to store cached files, must be writable by PHP
    
$cacheDir "cache/";
    
// cache filename constructed from MD5 hash of URL
    
$filename $cacheDir.md5($url);
    
// default to fetch the file
    
$fetch true;
    
// but if the file exists, don't fetch if it is recent enough
    
if (file_exists($filename))
    {
      
$fetch = (filemtime($filename) < (time()-$age));
    }
    
// fetch the file if required
    
if ($fetch)
    {
      
$maxRetry 2;
      do
      {
        
// shell to wget to fetch the file, storing the exit code in $error
        
exec("wget -N -O ".$filename." \"".$url."\"",$output,$error);
        
// update timestamp to now
        
exec("touch ".$filename);
        
// keep trying if wget failed and not reached $maxRetry
      
} while( $error && $maxRetry--);
    }
    
// return the filename only if wget did not fail
    
if (!$error)
    {
      return 
$filename;
    }
    else
    {
      
// as an error occured, delete the empty file so it is retried next time
      
unlink($filename);
      
// return false
      
return false;
    }
  }
  
$src $mainProduct["image_url"];
  
$src str_replace(" ","%20",$src);
  
$src cacheFetch($src,604800);
  
$img file_get_contents($src);
  
$oldImage imagecreatefromstring($img);
  
$oldX imagesx($oldImage);
  
$oldY imagesy($oldImage);
  if (
$oldX && $oldY)
  {
    
$newY 183;
    
$yFactor = ($newY $oldY);
    
$newX intval($oldX $yFactor);
    
$newImage imagecreatetruecolor($newX,$newY);
    
imagecopyresized($newImage$oldImage0,0,0,0$newX$newY$oldX$oldY);
    
imagejpeg($newImage,$src);
  }
  
header("Content-Type: image");
  print 
$img;
?>

Thanks!

Submitted by support on Fri, 2010-12-24 14:08.

Hi,

As it stands, the above would actually attempt to resize _every_ time, not just the first request when a cache fetch is made.

It's probably best to move the resize code within the cacheFetch function; as this way the resize process (which is relatively time consuming) will only happen the first time. Have a go with this:

<?php
  
require("includes/common.php");
  
// Get image based on product-ID
  
$id $_GET['id'];
  
$sql "SELECT * FROM `".$config_databaseTablePrefix."products` WHERE id = '".database_safe($id)."'";
  
$numRows database_querySelect($sql,$rows);
  
$product["products"] = $rows;
  
$mainProduct $product["products"][0];
  function 
cacheFetch($url,$age)
  {
    
// directory in which to store cached files, must be writable by PHP
    
$cacheDir "cache/";
    
// cache filename constructed from MD5 hash of URL
    
$filename $cacheDir.md5($url);
    
// default to fetch the file
    
$fetch true;
    
// but if the file exists, don't fetch if it is recent enough
    
if (file_exists($filename))
    {
      
$fetch = (filemtime($filename) < (time()-$age));
    }
    
// fetch the file if required
    
if ($fetch)
    {
      
$maxRetry 2;
      do
      {
        
// shell to wget to fetch the file, storing the exit code in $error
        
exec("wget -N -O ".$filename." \"".$url."\"",$output,$error);
        
// update timestamp to now
        
exec("touch ".$filename);
        
// keep trying if wget failed and not reached $maxRetry
      
} while( $error && $maxRetry--);
      
// resize
      
if (!$error)
      {
        
$img file_get_contents($filename);
        
$oldImage imagecreatefromstring($img);
        
$oldX imagesx($oldImage);
        
$oldY imagesy($oldImage);
        if (
$oldX && $oldY)
        {
          
$newY 183;
          
$yFactor = ($newY $oldY);
          
$newX intval($oldX $yFactor);
          
$newImage imagecreatetruecolor($newX,$newY);
          
imagecopyresized($newImage$oldImage0,0,0,0$newX$newY$oldX$oldY);
          
imagejpeg($newImage,$filename);
        }
      }
    }
    
// return the filename only if wget did not fail
    
if (!$error)
    {
      return 
$filename;
    }
    else
    {
      
// as an error occured, delete the empty file so it is retried next time
      
unlink($filename);
      
// return false
      
return false;
    }
  }
  
$src $mainProduct["image_url"];
  
$src str_replace(" ","%20",$src);
  
$src cacheFetch($src,604800);
  
$img file_get_contents($src);
  
header("Content-Type: image");
  print 
$img;
?>

Cheers,
David.
--
PriceTapestry.com

Submitted by wdekreij on Fri, 2010-12-24 14:40.

Thanks David! In addition to this code, add the following line:

<?php
 imagefill
($newImage00ImageColorAllocate($newImage255255255));
?>

right after
<?php
 $newImage 
imagecreatetruecolor($newX,$newY);
?>

Otherwise all transparant images will have a black background! :-)

Submitted by support on Fri, 2010-12-24 15:08.

Hi,

Just noticed that ID your original code isn't sanitised on the way in to the database, this line:

  $sql = "SELECT * FROM `".$config_databaseTablePrefix."products` WHERE id = '".$id."'";

...should be:

  $sql = "SELECT * FROM `".$config_databaseTablePrefix."products` WHERE id = '".database_safe($id)."'";

I've corrected this in each of the above posts as it's important to make sure that any variables contained in a URL are escaped before being used to construct SQL..!

Cheers,
David.
--
PriceTapestry.com

Submitted by Keeop on Tue, 2011-01-11 18:04.

Hi David,

I'm having problems using JQuery Thickbox 3.1 with the imagecache.php script - the image doesn't seem to be getting decoded properly. It's fine using the cache script everywhere else but just not with Thickbox. If I remove the imagecache.php script as the image source and just use the merchants' images, it's all OK.

Would you have any ideas regarding this?

Cheers.
Keeop

Submitted by support on Wed, 2011-01-12 09:44.

Hi Keeop,

Make sure that your image cache script contains the following line immediately before the cached image output is sent:

  header("Content-Type: image");

If that's already in place / doesn't help, could you post a snippet of how you are using an imagecache.php link in your jQuery code and I'll see if anything stands out...

Cheers,
David.
--
PriceTapestry.com

Submitted by Keeop on Wed, 2011-01-12 10:31.

Hi David,

Yeah, got that line but it still doesn't work. Here's the imagecache code (the last bit anyway):

  $src = $_GET["src"];
  $src = base64_decode($src);
  $img = cacheFetch($src,2419200);
  // update timestamp to now
  exec("touch ".$src);
  header("Content-Type: image/jpeg"); // have tried this as just : image as well
  readfile($img);

In calling the Thickbox code, it's just passing it the image along with a 'rel' tag to tell the JS to pass it to Thickbox, so the code is pretty standard:

print '<li id="thumbnail_'.$thumbCount.'"><a href="/imagecache.php?src='.base64_encode($product["image_url"]).'" rel="other-views" class="thickbox shown" title="'.$mainProduct["name"].'"><img id="thumb_'.$thumbCount.'" src="/imagecache.php?src='.base64_encode($product["image_url"]).'" alt="'.$mainProduct["name"].'" title="'.$mainProduct["name"].'" height="80" width="80" /></a></li>';

This brings up a Thickbox image of complete gibberish, as though the image encoding is incorrect, whereas..
print '<li id="thumbnail_'.$thumbCount.'"><a href="'.$product["image_url"].'" rel="other-views" class="thickbox " title="'.$mainProduct["name"].'"><img id="thumb_'.$thumbCount.'" src="'.$product["image_url"].'" alt="'.$mainProduct["name"].'" title="'.$mainProduct["name"].'" height="80" width="80" /></a></li>';

....using the merchant image url works fine.

If you want to see it, use this link:
{link saved}
Clicking on the first (leftmost) thumbnail uses the imagecache so will bring up the gibberish whereas clicking on any of the other thumbs uses the merchant url so works fine.

I can only imagine it's some sort of encoding problem as to what is being displayed. Maybe I need to pass it to Thickbox as a more standard url so maybe need to modify imagecache.php in some way to achieve this?

Thanks.
Keeop

Submitted by support on Wed, 2011-01-12 11:01.

Hi Keeop,

Whilst I can't see it making a difference, the first thing I would try is to make cached images appear more "normal" via .htaccess. Try adding this rule:

RewriteRule ^imagecache/(.*).jpg$ imagecache.php?src=$1 [L]

Then have a go with...

print '<li id="thumbnail_'.$thumbCount.'"><a href="/imagecache/'.base64_encode($product["image_url"]).'.jpg" rel="other-views" class="thickbox shown" title="'.$mainProduct["name"].'"><img id="thumb_'.$thumbCount.'" src="/imagecache.php/'.base64_encode($product["image_url"]).'.jpg" alt="'.$mainProduct["name"].'" title="'.$mainProduct["name"].'" height="80" width="80" /></a></li>';

Hope this helps!

Cheers,
David.
--
PriceTapestry.com

Submitted by Keeop on Wed, 2011-01-12 11:38.

Well, even when you don't think it will work it usually does, David, and it certainly does in this case! Thanks for that. I'm not sure why but it does!

Cheers.
Keeop

Submitted by wdekreij on Fri, 2011-03-04 05:22.

First I'll share some code to help others, there after I've a question myself as well :)

I've implemented the cache script in combination with .htacces so that the images appear to be on my server (e.g. domain.tld/images/423_productname.jpg). I've done so by adding the following line in the .htaccess file:
RewriteRule ^images/(.*)_(.*).jpg$ imageCacheID.php?id=$1 [L]

Then, my imageCacheID.php looks like this:

<?php
  
require("includes/common.php");
  
// Get image based on product-ID
  
$id $_GET['id'];
  
$sql "SELECT * FROM `".$config_databaseTablePrefix."products` WHERE id = '".$id."'";
  
$numRows database_querySelect($sql,$rows);
  
$product["products"] = $rows;
  
$mainProduct $product["products"][0];
  function 
cacheFetch($url,$age)
  {
    
// directory in which to store cached files, must be writable by PHP
    
$cacheDir "cache/";
    
// cache filename constructed from MD5 hash of URL
    
$filename $cacheDir.md5($url);
    
// default to fetch the file
    
$fetch true;
    
// but if the file exists, don't fetch if it is recent enough
    
if (file_exists($filename))
    {
      
$fetch = (filemtime($filename) < (time()-$age));
    }
    
// fetch the file if required
    
if ($fetch)
    {
      
$maxRetry 2;
      do
      {
        
// shell to wget to fetch the file, storing the exit code in $error
        
exec("wget -N -O ".$filename." \"".$url."\"",$output,$error);
        
// update timestamp to now
        
exec("touch ".$filename);
        
// keep trying if wget failed and not reached $maxRetry
      
} while( $error && $maxRetry--);
      
// resize
      
if (!$error)
      {
        
$img file_get_contents($filename);
        
$oldImage imagecreatefromstring($img);
        
$oldX imagesx($oldImage);
        
$oldY imagesy($oldImage);
        if (
$oldX && $oldY)
        {
          
$newY 183;
          
$yFactor = ($newY $oldY);
          
$newX intval($oldX $yFactor);
          
$newImage imagecreatetruecolor($newX,$newY);
          
imagefill($newImage00ImageColorAllocate($newImage255255255));
          
imagecopyresampled($newImage$oldImage0,0,0,0$newX$newY$oldX$oldY);
          
imagejpeg($newImage,$filename);
        }
      }
    }
    
// return the filename only if wget did not fail
    
if (!$error)
    {
      return 
$filename;
    }
    else
    {
      
// as an error occured, delete the empty file so it is retried next time
      
unlink($filename);
      
// return false
      
return false;
    }
  }
  
$src $mainProduct["image_url"];
  
$src str_replace(" ","%20",$src);
  
$src cacheFetch($src,604800);
  
$img file_get_contents($src);
  
header("Content-Type: image");
  print 
$img;
?>

Although, I do have a problem.. I just noticed that a server of one of the merchants is quite slow, which slows down my website as well.. So, I'm looking for a way to make a cron that caches all images that haven't been cached yet. I'll do this at nighttime, so that it's all superfast at daytime. Ideally I would even delete all products of which I can't get an image from, since my sites are focused on images.

Who has an idea on how to start on this?

Submitted by support on Fri, 2011-03-04 09:44.

Hi,

Should just take a simple script to make a request to imageCache.php for every image_url in the database, which won't cause any additional load on remote servers as already cached images will be returned locally. Have a go with something like this;

<?php
  set_time_limit
(0);
  require(
"includes/common.php");
  
$sql "SELECT id FROM `".$config_databaseTablePrefix."products` WHERE image_url <> ''";
  
database_querySelect($sql,$products);
  foreach(
$products as $product)
  {
    
$pullImage file_get_contents("http://www.yoursite.com/imageCacheID.php?id=".$product["id"]);
  }
?>

Hope this helps!

Cheers,
David.
--
PriceTapestry.com

Submitted by stevewales20 on Fri, 2011-03-04 21:57.

Cracking work lads, This was my next port of call, saved me a load of grief and time :)

Cheers.

Submitted by wdekreij on Mon, 2011-03-07 00:28.

Thanks! Works great :)

Submitted by Bigmac on Thu, 2011-04-21 13:19.

Hi David,

Does this work with 12/10A? I have tried adding the details above but can't seem to get it to work! It's more than likely to be me but thought I would ask just in case.

Cheers,

Hamish

Submitted by support on Thu, 2011-04-21 13:30.

Hi Hamish,

Should work fine - double check permissions on your cache folder (verify whether files are actually appearing in it, as that will tell you whether the problem is with the fetching, or the serving of the fetched files.

If you're not sure, let me know the URL (i'll remove it before publishing your reply) where image caching isn't working and I'll check it out for you...

Cheers,
David.
--
PriceTapestry.com

Submitted by Bigmac on Thu, 2011-04-21 15:47.

Hi again,

Files are being written to the cache folder and the image url is complete. If I try and pull the image up in my browser, I get a page full of this!

{code saved}

Any ideas?

Thanks again.

Cheers,

Hamish

Submitted by support on Thu, 2011-04-21 15:58.

Hi Hamish,

That looks like the image data is there, but is being preceded by a "Temporarily Unavailable" header for some reason. I appreciate this thread has become quite long so if you haven't already added it, one thing that has helped other users with this mod is to make sure that a Content-Type: image; header is sent by the browser to save the browser having to work it out for itself - in the post just above the mod is inserted immediately before:

  print $img;

...resulting in:

  header("Content-Type: image");
  print $img;

Hope this helps!

Cheers,
David.
--
PriceTapestry.com

Submitted by wilkins on Fri, 2011-04-29 08:57.

Hi david

Just started to implement this , working OK, could it also be put in the rss feed?

Brent

Submitted by support on Fri, 2011-04-29 13:29.

Hi Brent,

There's no specific image tag defined by RSS, but it can be included as HTML within the description element. Assuming that you have this (or similar) in your rss script:

    print "<description><![CDATA[".$product["description"]."]]></description>";

...you could use something like:

    print "<description><![CDATA[<img src='http://www.yoursite.com/imageCache.php?src=".urlencode($product["image_url"])."' /><p>".$product["description"]."</p>]]></description>";

Hope this helps!

Cheers,
David.
--
PriceTapestry.com

Submitted by Keeop on Fri, 2011-08-26 08:23.

Moved up to IIS7.5 on Windows 2008R2. It's taken me a while to even get WGET to function, but it now does and it downloading image files to the cache folder. However, these images are not displayed on the web page until I have subsequently refreshed the page for a second time. Sometimes I need to refresh 3 times! I don't know what on earth's going on so am wondering if any other users have solved similar problems? I shall also try the whole thing using an Apache server on the same O/S to see if there's any change.

Cheers.
Keeop

Submitted by Fuzzy on Sun, 2011-10-16 10:32.

Is it possible to cache the filename constructed from the actual filename?

I was using this code:

// cache filename constructed from URL replacing :, ., / and SPACE with _
    $temp = $url;
    $temp = str_replace(":"," ",$temp);
    $temp = str_replace("."," ",$temp);
    $temp = str_replace("/"," ",$temp);
    $temp = str_replace(" ","_",$temp);
    $filename = $cacheDir.$temp.$ext;

But something simpler like filename_img.jpg would be excellent. (where _img is appended to every filename)

Submitted by support on Mon, 2011-10-17 08:35.

Hi Fuzzy,

Unfortunately as many image URLs are dynamically constructed is quite often the case that there is no sensible filename with which to derive the cache filename as described; and further where image URLs are based on a product ID they may well be many duplicates between different merchants; so in this case I think I'd recommend in this case keeping with the method you've described which is probably the best compromise;

Cheers,
David.
--
PriceTapestry.com

Submitted by crounauer on Wed, 2011-12-21 10:25.

I have implemented a similar caching script which caches all product pages on my site. I modified the image caching script supplied by David. It has dramatically reduced my server loads, especially mySQL queries as I have a very large database at about 1.5GB. This has really worked for me but I found that space was quickly being used up due to the size of the site.

// Cache product page
$cacheDir = DIR_FS_CATALOG . "cache/";
$cacheFilename = $cacheDir . md5($_SERVER["REQUEST_URI"]);
$cacheTime = 4*60*60; // 4 hours
// Serve from the cache if it is younger than $cachetime
if (file_exists($cacheFilename) && time() - $cacheTime < filemtime($cacheFilename)) {
//echo "<div>Cached file</div>";
    include($cacheFilename);
    exit;
}
ob_start(); // Start the output buffer
Cached stuff goes here...
// Cache the contents to a file
$cached = fopen($cacheFilename, 'w');
fwrite($cached, ob_get_contents());
fclose($cached);
ob_end_flush(); // Send the output to the browser

I added the following script which cleanses the cache directory of all expired cache files and thought it be nice to share! It needs to be included ONCE at the bottom of a page which is called at a regular frequency. Hope this is of use to someone!

$cacheDir = DIR_FS_CATALOG . "cache/";
$cacheFilename = $cacheDir . md5($_SERVER["REQUEST_URI"]);
$cacheTime = 4*60*60; // 4 hours
// Delete cache
if ($handle = @opendir($cacheDir)) {
  while (false !== ($file = @readdir($handle))) {
    if ($file != '.' and $file != '..') {
if ( filemtime($cacheDir.$file) <= time() - ($cacheTime+1) ) {
@unlink($cacheDir . '/' . $file);
}
}
  }
  @closedir($handle);
}

I hope it's self explanatory.

Simon