health care, road trips, tech talk, occasional rant

image handling and forcing refresh

A site I recently finished had a classified ad section. Each ad has photos, and those photos needed thumbnails. This kind of requirement – for particular database objects to have associated images – happens all the time, so tools for implementing it are a standard part of the repertoire of a web site programmer.

At some point a few years ago I realized that I could eliminate a large chunk of implementation and maintenance work by not having any database representation of these images at all. Sometimes you can’t do that. If the images have captions or other properties particular to them you must save that info somewhere. But in simple cases like this classified ad project, they are just pictures of the product and that’s all you need to know about them.

In these cases I simply save them in the uploaded_images folder with a unique name generated from the unique id of the classified ad and their ordinal value – e.g. img_23_2.jpg for the second image for ad #23. The standard operations look like this:

  • change an image -> unlink (delete) the old and write the new
  • remove ad #23 -> unlink all images with the pattern img_23_*.jpg
  • reorder the images -> a little messy, a bunch of PHP renames

No database bookkeeping involved! The only information needed about an image is whether it exists or not, and all management can be easily done with file system functions. As you may know, the PHP move_uploaded_file() function requires that you give a name to the uploaded file, so it’s really quite a bit easier to use this generated name than to preserve the original name and do something complicated to keep track of that name.

This is all just awesome, until you build the back-end admin and notice that changing an image doesn’t seem to work! There’s usually a period where you think you have a bug in your code, but then you determine that the code is correct and the image on file is actually the correct new image, but your clever browser has cached the page with the old image and is happily showing without retrieving the changed image.

Usually, even though the page URL might be the same, the URL for the IMG tag within the page is different, which tells the browser that it at least has to get the new image. Here, the browser sees the same request for img_23_2.jpg, same as it ever was, so how is it to know that I the actual image is different?

The solution seems to be to convince your browser to not cache this page. If you are a veteran of these wars, immediately you are sad, because browser behavior is involved. But the show must go on, so after an hour or so of googling and tinkering I found a solution that seems to work, at least on the browsers I have tested so far.

Despite the documentation for this meta-tag saying exactly what I want, this did not work. It had no discernible effect on page behavior at all. I now realize that my issue is caching images rather than pages, but for the record:

  <meta http-equiv="no-cache">

The next attempt forces a page refresh/reload after 5 seconds. This kind of worked. But the delayed gratuitous redraw was very unsettling to me and it blew away the POST information I wanted for debug, so I hoped for a better way:

  <meta http-equiv="expires" content="5">

Many sites said that making each page URL unique would work. So I tried appending a parameter equal to the Unix timestamp to the page url (close enough to unique). Again, no discernible effect on page behavior. Come to think of it, clever Mac Firefox was noting that even though the page was different, the actual image request was exactly the same, so it was easily able to outsmart my feeble attempt to tell it what to do.

Finally, some friendly site (who’s name I regret that I did not record) gave me a comprehensive set of META requests that seem to do the trick to tell Mac Firefox at least to go back and retrieve the damn image again! Here ya go:

<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<META HTTP-EQUIV="Pragma" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Control" CONTENT="no-cache">
<META HTTP-EQUIV="Pragma-directive" CONTENT="no-cache">
<META HTTP-EQUIV="Cache-Directive" CONTENT="no-cache">
<META HTTP-EQUIV="Expires" CONTENT="0">