web-dev-qa-db-ja.com

ウェブブラウザに画像をキャッシュさせないようにする方法

バックグラウンド

私は、2つのプロボノWebサイト用に非常にシンプルなCGIベース(Perl)のコンテンツ管理ツールを作成して使用しています。 Webサイト管理者に、フィールド(日付、場所、タイトル、説明、リンクなど)を埋めて保存するイベントのHTMLフォームを提供します。そのフォームで、管理者がイベントに関連する画像をアップロードできるようにします。フォームを表示するHTMLページには、アップロードされた写真のプレビューも表示しています(HTML imgタグ)。

問題

この問題は、管理者が写真を変更するときに発生します。彼は「参照」ボタンを押すだけで、新しい写真を選択して[OK]を押すだけです。そして、これはうまく機能します。

画像がアップロードされると、バックエンドCGIがアップロードを処理し、フォームを適切に再読み込みします。

問題は、表示される画像がしない更新されることです。データベースが正しい画像を保持している場合でも、古い画像は引き続き表示されます。 IMAGE ISがWebブラウザでキャッシュされたという事実に絞り込みました。管理者がFirefox/Explorer/Safariの[再読み込み]ボタンを押すと、すべてが正常に更新され、新しい画像が表示されます。

私のソリューション-動作していません

私は、非常に過去の日付でHTTP Expires命令を書くことによってキャッシュを制御しようとしています。

Expires: Mon, 15 Sep 2003 1:00:00 GMT

私は管理者側であり、ページは常に期限切れであるため、ページの読み込みに少し時間がかかるかどうかはあまり気にしません。

しかし、これも機能しません。

ノート

画像をアップロードするとき、そのファイル名はデータベースに保存されません。これはImage.jpgに名前が変更されました(使用時に簡単に出力できるように)。既存のイメージを新しいイメージに置き換えても、名前は変わりません。画像ファイルの内容のみが変更されます。

Webサーバーは、ホスティングサービス/ ISPによって提供されます。 Apacheを使用します。

質問

ウェブブラウザに、画像ではなく、このページからのものをキャッシュしないように強制する方法はありますか?

私は実際にデータベースで「ファイル名を保存」するオプションをジャグリングしています。このように、画像が変更されると、IMGタグのsrcも変更されます。ただし、これにはサイト全体で多くの変更が必要であり、より良いソリューションがある場合はそれを行いません。また、アップロードされた新しい画像に同じ名前が付いている場合も機能しません(画像が少しフォトショップされて再アップロードされた場合など)。

113

アーミン・ロナッチャーは正しい考えを持っています。問題は、ランダムな文字列が衝突する可能性があることです。私は使うだろう:

<img src="picture.jpg?1222259157.415" alt="">

ここで、「1222259157.415」はサーバーの現在の時刻です。
Javaスクリプトでperformance.now()を使用して、またはPythonでtime.time()を使用して時間を生成します

171
epochwolf

簡単な修正:ランダムなクエリ文字列を画像に添付します:

<img src="foo.cgi?random=323527528432525.24234" alt="">

HTTP RFCの内容:

Cache-Control: no-cache

しかし、それはうまくいきません:)

45
Armin Ronacher

PHPのファイル修正時刻関数 を使用します。例:

echo <img  src='Images/image.png?" . filemtime('Images/image.png') . "'  />";

イメージを変更すると、変更されたタイムスタンプが異なるため、キャッシュされたイメージではなく新しいイメージが使用されます。

18
Rick

私は使うだろう:

<img src="picture.jpg?20130910043254">

ここで、「20130910043254」はファイルの変更時間です。

画像をアップロードするとき、そのファイル名はデータベースに保存されません。 Image.jpgに名前が変更されます(使用時に簡単に出力できるように)。既存のイメージを新しいイメージに置き換えても、名前は変わりません。画像ファイルの内容のみが変更されます。

2つのタイプの単純な解決策があると思います:1)最初に思い浮かぶもの(簡単に解決できるため、簡単な解決策)、2)物事を考えた後に終わるもの(簡単に解決できるため)つかいます)。どうやら、物事を熟考することを選択した場合、常に利益を得られるとは限りません。しかし、2番目のオプションはかなり過小評価されていると思います。 phpがとても人気がある理由を考えてみてください;)

15
x-yuri

画像を提供するためのプロキシスクリプトを作成することもできますが、それはもう少し手間がかかります。このようなもの:

HTML:

<img src="image.php?img=imageFile.jpg&some-random-number-262376" />

脚本:

// PHP
if( isset( $_GET['img'] ) && is_file( IMG_PATH . $_GET['img'] ) ) {

  // read contents
  $f = open( IMG_PATH . $_GET['img'] );
  $img = $f.read();
  $f.close();

  // no-cache headers - complete set
  // these copied from [php.net/header][1], tested myself - works
  header("Expires: Sat, 26 Jul 1997 05:00:00 GMT"); // Some time in the past
  header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); 
  header("Cache-Control: no-store, no-cache, must-revalidate"); 
  header("Cache-Control: post-check=0, pre-check=0", false); 
  header("Pragma: no-cache"); 

  // image related headers
  header('Accept-Ranges: bytes');
  header('Content-Length: '.strlen( $img )); // How many bytes we're going to send
  header('Content-Type: image/jpeg'); // or image/png etc

  // actual image
  echo $img;
  exit();
}

実際には、画像srcのキャッシュなしヘッダーまたは乱数のいずれかで十分ですが、防弾にしたいので。

7
Mindeh

私は新しいコーダーですが、ブラウザがキャッシュしてウェブカメラのビューを保持するのを止めるために、ここで思いつきました。

<meta Http-Equiv="Cache" content="no-cache">
<meta Http-Equiv="Pragma-Control" content="no-cache">
<meta Http-Equiv="Cache-directive" Content="no-cache">
<meta Http-Equiv="Pragma-directive" Content="no-cache">
<meta Http-Equiv="Cache-Control" Content="no-cache">
<meta Http-Equiv="Pragma" Content="no-cache">
<meta Http-Equiv="Expires" Content="0">
<meta Http-Equiv="Pragma-directive: no-cache">
<meta Http-Equiv="Cache-directive: no-cache">

どのブラウザで何が機能するかはわかりませんが、一部のブラウザでは機能します:IE:Webページが更新されたとき、およびWebサイトが(更新なしで)再訪されたときに機能します。 CHROME:Webページが更新された場合にのみ機能します(再訪問後でも)。 SAFARIとiPad:機能しません。履歴とWebデータを消去する必要があります。

SAFARI/iPadのアイデアはありますか?

6
Timmy T.

Class = "NO-CACHE"を使用します

サンプルhtml:

<div>
    <img class="NO-CACHE" src="images/img1.jpg" />
    <img class="NO-CACHE" src="images/imgLogo.jpg" />
</div>

jQuery:

    $(document).ready(function ()
    {           
        $('.NO-CACHE').attr('src',function () { return $(this).attr('src') + "?a=" + Math.random() });
    });

javascript:

var nods = document.getElementsByClassName('NO-CACHE');
for (var i = 0; i < nods.length; i++)
{
    nods[i].attributes['src'].value += "?a=" + Math.random();
}

結果:src = "images/img1.jpg" => src = "images/img1.jpg?a = 0.08749723793963926"

6

私はウェブ上のすべての答えをチェックしましたが、最良のものは次のように見えました:(実際にはそうではありません)

<img src="image.png?cache=none">

最初は。

ただし、cache = noneパラメーター(静的な「なし」のWord)を追加しても、何も影響はなく、ブラウザーはキャッシュからロードされます。

この問題の解決策は次のとおりです。

<img src="image.png?nocache=<?php echo time(); ?>">

基本的に、Unixタイムスタンプを追加してパラメータを動的に設定し、キャッシュを使用しない場合、機能しました。

しかし、私の問題は少し異なっていました:私はその場で生成されたPHPチャート画像をロードし、$ _ GETパラメータでページを制御していました。 URL GETパラメーターが同じ場合はキャッシュからイメージを読み取り、GETパラメーターが変更された場合はキャッシュしないようにしました。

この問題を解決するために、$ _ GETをハッシュする必要がありましたが、配列であるため、ここに解決策があります。

$chart_hash = md5(implode('-', $_GET));
echo "<img src='/images/mychart.png?hash=$chart_hash'>";

編集

上記のソリューションは問題なく機能しますが、ファイルが変更されるまでキャッシュバージョンを提供したい場合があります。 (上記のソリューションでは、そのイメージのキャッシュを完全に無効にします)したがって、ブラウザーからキャッシュされたイメージを提供するまで、イメージファイルの使用に変更があります。

echo "<img src='/images/mychart.png?hash=" . filemtime('mychart.png') . "'>";

filemtime()はファイル変更時間を取得します。

4
Tarik

画像をアップロードするとき、そのファイル名はデータベースに保存されません。 Image.jpgに名前が変更されます(使用時に簡単に出力できるように)。

これを変更すると、問題は修正されました。上記で提案したソリューションと同様に、タイムスタンプを使用します。Image- <timestamp> .jpg

おそらく、画像と同じファイル名を保持することで回避している問題はすべて克服できますが、それが何であるかは言いません。

4
AmbroseChapel

問題は、Expires:ヘッダーにもかかわらず、ブラウザがキャッシュをチェックするのではなく、更新前の画像のメモリ内コピーを再利用していることです。

ストアのようなサイトの管理バックエンドで製品画像をアップロードする状況は非常に似ていましたが、私の場合、他の人のURL変更技術を使用せずにJavaScriptを使用して画像を強制的に更新することが最善の選択肢であると判断しましたすでにここで言及しています。代わりに、画像URLをIFRAMEのウィンドウのlocation.reload(true)と呼ばれる非表示のIFRAMEに入れてから、ページ上の画像を置き換えました。これにより、現在のページだけでなく、アクセスした後のページでも、クライアントまたはサーバーがURLクエリ文字列またはフラグメント識別子パラメーターを覚える必要なく、画像が強制的に更新されます。

これを行うためのコードを自分の回答に投稿しました here

2
Doin

タイムスタンプを追加<img src="picture.jpg?t=<?php echo time();?>">

最後に常にファイルに乱数を与え、キャッシュを停止します

2
BritishSam

元の質問は、テキスト情報とともに保存された画像に関するものと思われます。したがって、src = ... urlを生成するときにテキストコンテキストにアクセスできる場合は、無意味なランダムまたはタイムスタンプではなく、イメージバイトのCRC32を保存/使用することを検討してください。その後、大量の画像を含むページが表示されている場合、更新された画像のみがリロードされます。最終的に、CRCの保存が不可能な場合は、実行時に計算してURLに追加できます。

1
jbw

あなたとクライアントの間に不適切な動作の透明なプロキシが存在する可能性があるため、画像がキャッシュされないことを完全に保証する唯一の方法は、クエリ文字列として、または道。

そのタイムスタンプが画像の最終更新時間に対応する場合、必要なときにキャッシュして、適切なタイミングで新しい画像を提供できます。

1
user7375

私の観点からは、画像のキャッシュを無効にすることは悪い考えです。まったく。

ここでの根本的な問題は、サーバー側で画像が更新されたときに、ブラウザに画像を更新させる方法です。

繰り返しますが、私の個人的な観点から、最良の解決策は画像への直接アクセスを無効にすることです。代わりに、サーバー側のフィルター/サーブレット/他の同様のツール/サービスを介して画像にアクセスします。

私の場合、それは画像を返し、応答でETagを添付する休息サービスです。サービスはすべてのファイルのハッシュを保持します。ファイルが変更されると、ハッシュが更新されます。最新のすべてのブラウザーで完全に機能します。はい、実装するには時間がかかりますが、それだけの価値はあります。

唯一の例外-ファビコンです。いくつかの理由で、機能しません。ブラウザにサーバー側からキャッシュを強制的に更新させることはできませんでした。 ETags、Cache Control、Expires、Pragma headers、何も助けなかった。

この場合、ランダム/バージョンパラメータをURLに追加することが唯一の解決策のようです。

1
Alexandr

理想的には、コンテンツを同期するオプションを備えたボタン/キーバインディング/メニューを各Webページに追加する必要があります。

これを行うには、同期が必要なリソースを追跡し、xhrを使用して動的クエリ文字列で画像をプローブするか、動的クエリ文字列を使用してsrcで実行時に画像を作成します。次に、ブロードキャストメカニズムを使用して、リソースを使用しているWebページのすべてのコンポーネントを更新して、URLに動的クエリ文字列を追加したリソースを使用するように更新します。

単純な例は次のようになります。

通常、画像は表示およびキャッシュされますが、ユーザーがボタンを押すと、時間クエリ文字列が付加されたxhrリクエストがリソースに送信されます。プレスごとに時間が異なると想定できるため、クエリに基づいてリソースがサーバー側で動的に生成されるか、静的であるかを判断できないため、ブラウザがキャッシュをバイパスすることを確認しますクエリを無視するリソース。

その結果、すべてのユーザーが常にリソースリクエストで攻撃されるのを避けることができますが、同時に、同期していないと思われる場合にユーザーがリソースを更新するメカニズムを許可します。

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="mobile-web-app-capable" content="yes" />        
    <title>Resource Synchronization Test</title>
    <script>
function sync() {
    var xhr = new XMLHttpRequest;
    xhr.onreadystatechange = function() {
        if (this.readyState == 4 && this.status == 200) {            
            var images = document.getElementsByClassName("depends-on-resource");

            for (var i = 0; i < images.length; ++i) {
                var image = images[i];
                if (image.getAttribute('data-resource-name') == 'resource.bmp') {
                    image.src = 'resource.bmp?i=' + new Date().getTime();                
                }
            }
        }
    }
    xhr.open('GET', 'resource.bmp', true);
    xhr.send();
}
    </script>
  </head>
  <body>
    <img class="depends-on-resource" data-resource-name="resource.bmp" src="resource.bmp"></img>
    <button onclick="sync()">sync</button>
  </body>
</html>
0
Dmitry