PHPで画像を動的に出力する際にキャッシュさせない

シェアする

  • このエントリーをはてなブックマークに追加
  • Evernoteに保存Evernoteに保存

PHPのGDを使って合成した画像を出力するスクリプトを作って、テスト用に<img>タグをたくさんならべて、変化をチェックしてみた。

しかし、URLが変わらないためか、いくつかの画像が表示しきれなかったり、他と同じだったり・・・

単発でスクリプトを叩くと、ちゃんと表示されるので、どうやらブラウザのキャッシュが効いているようである。

色々調べてみたが、完全にキャッシュをクリアというかキャッシュを使わせないことは難しいらしい。

答え

PHPでheader情報を出力する際に”Etag”と改行コードを入れる。

<?php
/* 保存されている画像をキャッシュされにくく出力するスクリプト */
$sFileName = "image.png";
$sFilePath = "./img/" . $sFileName ;
if(File_Exists($sFilePath)){
//画像データの読み込み
if(!($fp = fopen($sFilePath,'rb'))) die();
$iFileSize = FileSize($sFilePath);
$img = fread($fp,$iFileSize);
fclose($fp);
$sEtag    = md5_file($sFilePath);     //ファイルをMD5したものをEtagのIDとする
$aImgInfo = getImageSize($sSavePath); //ファイルの情報を取得
$sMIME    = $aImgInfo['mime'];        //ファイルの種類(MIME)を取得
header("Cache-Control: no-cache, must-revalidate\n\n");           // HTTP/1.1
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT\n\n");             // 過去の日付
header("Content-type: {$sMIME}\n\n");                             // ファイルのMIME出力
header("Content-Disposition: inline; filename={$sFileName}\n\n"); // 保存時ファイル名
header("Etag: \"{$sEtag}\"\n\n");                                 // Etagの出力
print ($img); //画像データを出力
}
?>

さすがに F5キーとか、Ctrl+F5キーとかで、激しく更新すると×マークの画像がでることがあるが、それでも以前より表示は安定した(キャッシュされずに最新の画像が表示された)。

注意としては

サーバ負荷を減らすためにも、一度作成した画像はサーバ側でも保存(キャッシュ)しておいて、過去に一度リクエスト済みの画像は改めて(作成)処理せずに、保存した画像(キャッシュ画像)を使うのがベターなのである。

その際に Etagを見て、Etagと同じ画像ならデータを出力せずに、”304 Not Modified”のヘッダー情報を返せばよりサーバの負荷やレスポンスが軽くなるハズ。

色々試していたわけだが、にっちもさっちもだったので、Firefoxの”Live HTTP headers”プラグインを使って、他のサイトにある画像だけを表示してヘッダー情報を調べたら Etagが送信されていた。

動的HTMLページを作成する際に、昔ハマった記憶がよみがえる。

色々試したけど、最初NGだった結果

  • header情報で Cache-Control を送信してもキャッシュされることがある
  • header情報で Expires を送信してもキャッシュされることがある
  • imgタグのsrc属性内にランダムな数値を付加してもキャッシュされることがある
    (例:src=’./image.png?aadf0eadf0ee’)

header情報に改行コードを入れてみ

ヘッダーの最後に改行コードを2つ(”\n\n”を)入れてみたのですが、どうもこれがないとヘッダーが区別されないのか、入れたら少しよくなった。

header(“Cache-Control: no-cache, must-revalidate”);

header(“Cache-Control: no-cache, must-revalidate\n\n”);

Etagを入れてみた

Etagについて詳しくは調べてもらうとして、一言でいうとEtagはデータのID番号というか版番号(バージョン番号)みたいなもの。

つまり、Etagが同じなら同じデータであるということですな。

ヘッダーのやり取りのサンプルで php.netのロゴ画像を表示する際のヘッダー情報はこんな感じ。

■ブラウザからの画像のリクエスト

以下は「http://jp.php.net/images/php.gifの画像が欲すぃの。手元の画像のEtagは”189c018-9db-f8269940″よ。」の意味。

http://jp.php.net/images/php.gif

GET /images/php.gif HTTP/1.1

Host: jp.php.net

User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; ja; rv:1.8.1.15) Gecko/20080623 Firefox/2.0.0.15

Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5

Accept-Language: ja,en-us;q=0.7,en;q=0.3

Accept-Encoding: gzip,deflate

Accept-Charset: Shift_JIS,utf-8;q=0.7,*;q=0.7

Keep-Alive: 300

Connection: keep-alive

Cookie: LAST_LANG=ja; COUNTRY=JPN%2C122.17.95.69

If-Modified-Since: Mon, 06 Mar 2006 21:11:25 GMT

If-None-Match: “189c018-9db-f8269940”

Cache-Control: max-age=0

■サーバからのレスポンス

以下は「Etagが同じなので、変更なし。データは送らないのでキャッシュを使ってね」の意味。

HTTP/1.x 304 Not Modified

Date: Wed, 16 Jul 2008 14:28:06 GMT

Server: Apache

Connection: Keep-Alive

Keep-Alive: timeout=15, max=100

Etag: “189c018-9db-f8269940”

いずれもFirefoxのLive HTTP headers プラグインで見れます。

関連記事

jQueryの”.getJSON”で”200 OK”のレスポンスにデータがない... Google Gadgetで、jQueryの".getJSON"メソッドを使って別ドメインから(クロスドメインで)、JSONデータが受け取れない際に注意すること。 HTTPリクエスト・ヘッダに"Origin"が渡されていたら、レスポンス・ヘッダに"Access-Control-Allow-...

スポンサーリンク
レクタングル(大)広告

シェアする

  • このエントリーをはてなブックマークに追加
  • Evernoteに保存Evernoteに保存
スポンサーリンク
レクタングル(大)広告