12

If I load the nextimg URL manually in the browser, it gives a new picture every time I reload. But this bit of code shows the same image every iteration of draw().

How can I force myimg not to be cached?

<html>
  <head>
    <script type="text/javascript">
      function draw(){
        var canvas = document.getElementById('canv');
        var ctx = canvas.getContext('2d');
        var rx;
        var ry;
        var i;

        myimg = new Image();
        myimg.src = 'http://ohm:8080/cgi-bin/nextimg'

        rx=Math.floor(Math.random()*100)*10
        ry=Math.floor(Math.random()*100)*10
        ctx.drawImage(myimg,rx,ry);
        window.setTimeout('draw()',0);
      }
    </script>
  </head>
  <body onload="draw();">
    <canvas id="canv" width="1024" height="1024"></canvas>
  </body>
</html>

5 Answers 5

26

The easiest way is to sling an ever-changing querystring onto the end:

var url = 'http://.../?' + escape(new Date())

Some people prefer using Math.random() for that instead of escape(new Date()). But the correct way is probably to alter the headers the web server sends to disallow caching.

Sign up to request clarification or add additional context in comments.

5 Comments

Dan, kind of old post but I surely owe you a beer on this one. Not exactly my situation, but gave me great idea. Thanks!
I had to use it without the ending slash : var url = 'http;//.../something.png'+'?'+Math.random(), but it's working fine ! +1 for the headers advisory :)
Does Cache-Control: no-cache help here? While I appreciate the nifty workaround I can't help but wonder if this is the best we can do right now.. what kind of hacks did we use in the moon lander code then?
escape is deprecated, please use encodeURI instead. See w3schools.com/jsref/jsref_escape.asp
@evilSnobu Your question intrigued me, so I have necro answered it 7 years later.
9

You can't stop it from caching the image altogether within Javascript. But, you can toy with the src/address of the image to force it to cache anew:

[Image].src = 'image.png?' + (new Date()).getTime();

You can probably take any of the Ajax cache solutions and apply it here.

Comments

5

That actually sounds like a bug in the browser -- you could file at http://bugs.webkit.org if it's in Safari or https://bugzilla.mozilla.org/ for Firefox. Why do i say potential browser bug? Because the browser realises it should not be caching on reload, yet it does give you a cached copy of the image when you request it programmatically.

That said are you sure you're actually drawing anything? the Canvas.drawImage API will not wait for an image to load, and is spec'd to not draw if the image has not completely loaded when you try to use it.

A better practice is something like:

    var myimg = new Image();
    myimg.onload = function() {
        var rx=Math.floor(Math.random()*100)*10
        var ry=Math.floor(Math.random()*100)*10
        ctx.drawImage(myimg,rx,ry);
        window.setTimeout(draw,0);
    }
    myimg.src = 'http://ohm:8080/cgi-bin/nextimg'

(You can also just pass draw as an argument to setTimeout rather than using a string, which will save reparsing and compiling the same string over and over again.)

Comments

0

There are actually two caches you need to bypass here: One is the regular HTTP cache, that you can avoid by using the correct HTTP headers on the image. But you've also got to stop the browser from re-using an in-memory copy of the image; if it decides it can do that it will never even get to the point of querying its cache, so HTTP headers won't help.

To prevent this, you can use either a changing querystring or a changing fragment identifier.

See my post here for more details.

Comments

0

Here is a 2024 update on handling this situation that I've used to organize and address some of the answers and comments on this question.

Client Side Solutions

Assume you have:

var url = 'http://website.com/20.jpg'

The easiest way to handle this if you have no control over the server is:

var date = encodeURI(new Date())
img.src = `${url}?${date}`

The following will also usually work but has the (remote) possibility of collisions between equivalent random numbers. If you want to play it 100% safe, just use the first approach:

var rand = Math.random()
img.src = `${url}?${rand}`

Someone in the comments of Dan's answer suggested using Cache-Control: no-cache for client side requests. Unfortunately, there are two problems with this approach:

  1. It requires the server to have reliably implemented the If-Modified-Since and If-None-Match request headers, as explained in this answer.
  2. Even if the server has implemented these headers correctly (or even if you decide to go with the more robust Cache-Control: no-store header that doesn't require those headers), there is no reliable way to tell the browser you want to use the Cache-Control header for image src requests, as explored in the answers to this question. If you are doing fetch requests, then, by all means, use the approach in this answer to set the Cache-Control header.

Server Side Solutions

If you have control over the server, you can set the Cache-Control header server side for requests. As explained above, if you want optimal caching, you need to implement If-Modified-Since and If-None-Match and use them alongside Cache-Control: no-cache. If you just want to eliminate caching altogether, you can simply set Cache-Control: no-store.

Here is how you could do that in Go:

func ServeMedia(w http.ResponseWriter, b []byte) {
    w.Header().Set("Content-Type", "application/octet-stream")
    // Eliminate caching so that reused image names will refresh.
    w.Header().Set("Cache-Control", "no-store")
    w.Write(b)
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.