views:

619

answers:

5

I'm totally new to how to cache images.

I output all images in a gallery with php, and want the images already shown, to be cached by the browser, so the php-script don't have to output the same image again. All I want is the images to show up faster.

when calling an image I do like this:

<img src="showImage.php?id=601">

and the showImage.php-file does:

$resultat = mysql_query("
    SELECT filename, id
    FROM Media 
    WHERE id = '".$_GET['id']."'
");
$data = mysql_fetch_assoc($resultat);

...

//Only if the user are logged in
if(isset($_SESSION['user'])){
    header("Content-Type: image/jpeg");

    //$data['filename'] can be = dsSGKLMsgKkD3325J.jpg
    echo(file_get_contents("images/".$data['filename']."")); 
}
+4  A: 

First of all, if you're using sessions, you must disable session_cache_limiter (by setting it to none or public). Headers it sends are the worst case of voodoo programming I've ever seen.

session_cache_limiter('none');

Then send Cache-Control: max-age=number_of_seconds and optionally equivalent Expires: header.

header('Cache-control: max-age='.(60*60*24*365));
header('Expires: '.gmdate(DATE_RFC1123,time()+60*60*24*365));

To get best cacheability, send Last-Modified header and reply with status 304 and empty body if browser sends If-Modified-Since header.

header('Last-Modified: '.gmdate(DATE_RFC1123,filemtime($path_to_image));

This is cheating a bit (doesn't verify the date), but is valid as long as you don't mind browsers keeping cached file forever:

if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
   header('HTTP/1.1 304 Not Modified');
   die();
}
porneL
Yes, I'm using `sessions`. Hm, it looks like that you say is what I need, but I don't understand how to get that together to working code. How do I write if I want the browser to cache the image for a year?
Johan
+2  A: 

You could store the generated images in a directory called "showImage" so that you would embed them like this

<img src="showimage/601.jpg" />

Then you place a .htaccess file in the very same directory that will call showImage.php?id= in case the file does not exist, e.g.:

<IfModule mod_rewrite.c>
   RewriteEngine On
   RewriteCond %{REQUEST_FILENAME} !-f
   RewriteRule ^(.*)\.jpg$ showImage.php?id=$1 [QSA,L]
</IfModule>


Just read in your comment that you want to do client side caching: just set the caching-related HTTP headers according to http://www.mnot.net/cache%5Fdocs/


Please be aware that your query is vulnerable to SQL injections, because you do not sanitize/escape $_GET['id']: http://php.net/mysql%5Freal%5Fescape%5Fstring

middus
+1  A: 

Please do not address images are some id'ed resource. use absolute urls for images, preferably in a subdomain, preferably cookie less. The browser will do the caching on the images. A neat trick to load images faster on websites is to put it on some CDN or other site. This because browsers limit the number of parallel request threads to one domain.

Another neat way of working with images is spriting, look it up. It saves a lot of bandwidth and also requests.

You could also use direct bitmap loading if speed is so crucial. This is not advised for large images though. If its icons and small images/gifs that you are loading. You can use bitmaps directly on the page.

Ritesh M Nayak
+2  A: 

If you are using php to check if the user is logged in before outputting the message, then you don't want the browser to cache the image.

The entire point of caching is to call the server once and then never call it again. If the browser caches the image, it won't call the server and your script won't run. Instead, the browser will pull your image from cache and display it, even if the user is no longer logged in. This could potentially be a very big security hole.

Imagist
Hm, that's a good point. But it would be OK to store the image an hour tough.
Johan
Even an hour isn't secure, all that does is hope that hackers don't attempt to hack at a certain time. However, from what I can see on http://www.mnot.net/cache_docs/#SCRIPT , it looks like you can force authentication with caching by using `Cache-control: public, no-cache;` in your HTTP header (`no-cache` doesn't actually mean no caching; the names are a bit off).
Imagist
+2  A: 

Here's some code I use for 304 header support:

  /**
   * @return false if not cached or modified, true otherwise.
   * @param bool check_request set this to true if you want to check the client's request headers and "return" 304 if it makes sense. will only output the cache response headers otherwise.
   **/     
  protected function sendHTTPCacheHeaders($cache_file_name, $check_request = false)
  {
    $mtime = @filemtime($cache_file_name);

    if($mtime > 0)
    {
      $gmt_mtime = gmdate('D, d M Y H:i:s', $mtime) . ' GMT';
      $etag = sprintf('%08x-%08x', crc32($cache_file_name), $mtime);

      header('ETag: "' . $etag . '"');
      header('Last-Modified: ' . $gmt_mtime);
      header('Cache-Control: private');
      // we don't send an "Expires:" header to make clients/browsers use if-modified-since and/or if-none-match

      if($check_request)
      {
        if(isset($_SERVER['HTTP_IF_NONE_MATCH']) && !empty($_SERVER['HTTP_IF_NONE_MATCH']))
        {
          $tmp = explode(';', $_SERVER['HTTP_IF_NONE_MATCH']); // IE fix!
          if(!empty($tmp[0]) && strtotime($tmp[0]) == strtotime($gmt_mtime))
          {
            header('HTTP/1.1 304 Not Modified');
            return false;
          }
        }

        if(isset($_SERVER['HTTP_IF_NONE_MATCH']))
        {
          if(str_replace(array('\"', '"'), '', $_SERVER['HTTP_IF_NONE_MATCH']) == $etag)
          {
            header('HTTP/1.1 304 Not Modified');
            return false;
          }
        }
      }
    }

    return true;
  }
KiNgMaR