Cara memaksa browser web TIDAK menyimpan gambar dalam cache

124

Latar Belakang

Saya menulis dan menggunakan alat manajemen konten berbasis CGI (Perl) yang sangat sederhana untuk dua situs web pro-bono. Ini memberi administrator situs web formulir HTML untuk acara di mana mereka mengisi bidang (tanggal, tempat, judul, deskripsi, tautan, dll.) Dan menyimpannya. Pada formulir itu saya mengizinkan administrator untuk mengunggah gambar yang terkait dengan acara tersebut. Pada halaman HTML yang menampilkan formulir, saya juga menampilkan pratinjau dari gambar yang diunggah (tag img HTML).

Masalah

Masalah terjadi ketika administrator ingin mengubah gambar. Dia hanya perlu menekan tombol "browse", pilih gambar baru dan tekan ok. Dan ini bekerja dengan baik.

Setelah gambar diunggah, CGI back-end saya menangani unggahan dan memuat ulang formulir dengan benar.

Masalahnya adalah gambar yang ditampilkan tidak refresh. Gambar lama masih ditampilkan, meskipun database menyimpan gambar yang benar. Saya telah mempersempitnya menjadi fakta bahwa GAMBAR DI-cache di browser web. Jika administrator menekan tombol RELOAD di Firefox / Explorer / Safari, semuanya akan disegarkan dengan baik dan gambar baru baru saja muncul.

Solusi Saya - Tidak Berfungsi

Saya mencoba mengontrol cache dengan menulis instruksi HTTP Expires dengan tanggal yang sangat jauh di masa lalu.

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

Ingatlah bahwa saya berada di sisi administrasi dan saya tidak terlalu peduli jika halaman membutuhkan waktu lebih lama untuk memuat karena selalu kedaluwarsa.

Tapi, ini juga tidak berhasil.

Catatan

Saat mengunggah gambar, nama filenya tidak disimpan di database. Ini diubah namanya menjadi Image.jpg (untuk saat menggunakannya). Saat mengganti gambar yang ada dengan yang baru, namanya juga tidak berubah. Hanya konten file gambar yang berubah.

Server web disediakan oleh layanan hosting / ISP. Ini menggunakan Apache.

Pertanyaan

Adakah cara untuk memaksa browser web agar TIDAK menyimpan sesuatu dari halaman ini, bahkan gambar pun tidak?

Saya menyulap dengan opsi untuk benar-benar "menyimpan nama file" dengan database. Dengan cara ini, jika gambar diubah, src dari tag IMG juga akan berubah. Namun, ini membutuhkan banyak perubahan di seluruh situs dan saya lebih suka tidak melakukannya jika saya memiliki solusi yang lebih baik. Selain itu, ini tetap tidak akan berfungsi jika gambar baru yang diunggah memiliki nama yang sama (katakanlah gambar di-photoshop sedikit dan diunggah ulang).

Philibert Perusse
sumber
Saya pikir pertanyaan ini bisa dikerjakan ulang untuk menghapus semua "konteks sekarang tidak berguna" yang saya masukkan di sana beberapa waktu lalu ketika saya menulisnya. Saya pikir judulnya benar, dan jawaban teratas juga. Tetapi pertanyaan tersebut telah ditulis pada awalnya untuk memberikan banyak ruang untuk berbagai jenis jawaban dan tidak dapat mengharapkan solusi yang sesederhana itu. Oleh karena itu pertanyaannya agak berbelit-belit bagi orang-orang yang datang ke sana untuk mendapatkan jawabannya.
Philibert Perusse

Jawaban:

186

Armin Ronacher punya ide yang benar. Masalahnya adalah string acak bisa bertabrakan. Saya akan menggunakan:

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

Di mana "1222259157.415" adalah waktu saat ini di server.
Hasilkan waktu dengan Javascript dengan performance.now()atau dengan Python dengantime.time()

epochwolf
sumber
32
Satu tambahan penting adalah Anda tidak pernah bisa memaksa browser untuk melakukan apapun. Yang bisa Anda lakukan hanyalah memberikan saran yang ramah. Terserah browser dan pengguna untuk benar-benar mengikuti saran tersebut. Browser bebas untuk mengabaikan ini, atau pengguna dapat menimpa default.
Joel Coehoorn
8
Joel, lebih baik Anda menambahkannya dalam jawaban Anda sendiri.
epochwolf
1
Hanya pemikiran, dan terlalu terlambat untuk datang ke pesta di sini - tetapi karena Anda memiliki kendali atas perubahan gambar, mungkin akan lebih baik untuk mengganti nama gambar saat diperbarui, daripada menambahkan string kueri. Jadi: 1222259157.jpg misalnya, bukan gambar.jpg? 1222259157. Dengan cara itu itu diperbarui dan tetapi kembali di-cache setelah dikunjungi kembali.
danjah
43
Daripada menambahkan waktu server, yang akan mencegah cache sama sekali, mengapa tidak menambahkan waktu terakhir diubah file setelah '?'. Dengan begitu, gambar akan disimpan dalam cache secara normal hingga perubahan berikutnya.
Lakukan
3
Komentar terakhir oleh Doin harus di-upvoting! Smart caching memang penting, mengapa seseorang harus mengunduh berulang kali file yang sama?
f. Arcarese
46

Perbaikan sederhana: Lampirkan string kueri acak ke gambar:

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

Apa yang dikatakan HTTP RFC:

Cache-Control: no-cache

Tapi itu tidak berhasil dengan baik :)

Armin Ronacher
sumber
1
bagaimana cara memberikan Cache-Control: no-cache di tag html <img> normal?
P Satish Patro
Apakah itu perlu datang di header respon?
P Satish Patro
22

Saya menggunakan fungsi waktu modifikasi file PHP , misalnya:

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

Jika Anda mengubah gambar, maka gambar baru yang digunakan daripada gambar yang disimpan dalam cache, karena stempel waktu yang dimodifikasi berbeda.

Rick
sumber
Tip yang bagus. Anda memiliki cache dan gambar yang dikirim ulang ke klien jika waktu yang diubah berubah (ditimpa atau diubah).
Vasilis Lourdas
lihat di sini untuk opsi lain stackoverflow.com/questions/56588850/…
drooh
Benar-benar berhasil. Kawan tip yang hebat.
Khurram Shaikh
Sempurna! Hanya tip tambahan: <img src = "images / image.png? <? Php echo filemtime ('images / image.jpg')?>">
Rica Gurgel
16

Saya akan menggunakan:

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

dengan "20130910043254" adalah waktu modifikasi file.

Saat mengunggah gambar, nama filenya tidak disimpan di database. Ini berganti nama menjadi Image.jpg (untuk memudahkan saat menggunakannya). Saat mengganti gambar yang ada dengan yang baru, namanya juga tidak berubah. Hanya konten file gambar yang berubah.

Menurut saya ada dua jenis solusi sederhana: 1) solusi yang pertama kali muncul dalam pikiran (solusi langsung, karena mudah ditemukan), 2) solusi yang akan Anda dapatkan setelah memikirkan semuanya (karena mudah dilakukan). menggunakan). Rupanya, Anda tidak akan selalu mendapat manfaat jika memilih untuk memikirkan semuanya. Tapi opsi kedua agak diremehkan, saya yakin. Coba pikirkan mengapa phpbegitu populer;)

x-yuri
sumber
1
+1 Saya pikir itu ide yang jauh lebih baik daripada jawaban lain karena dengan menggunakan waktu modifikasi terakhir, server Anda tidak akan mencoba memberikan gambar yang sama beberapa kali ke browser yang sama ketika tidak ada yang berubah pada gambar. Ada sedikit biaya tambahan untuk mengekstrak waktu tulis terakhir gambar setiap kali ada permintaan, tetapi untuk gambar besar, lebih baik daripada harus mengembalikan gambar setiap kali dan saat gambar berubah, pengguna akan mendapatkan gambar baru. Terima kasih atas tambahan kecil yang menyenangkan ini.
Samuel
Nah, seseorang mungkin menyimpan waktu modifikasi bersama dengan jalur ke gambar dalam database. Jadi biaya overhead mungkin bahkan kurang signifikan. Di sisi lain, jika ini adalah gambar yang merupakan bagian dari kode sumber yang kita bicarakan, orang mungkin juga menyimpan waktu modifikasinya. Dengan membuat skrip dengan waktu modifikasi gambar (misalnya images.php). Skrip ini harus dibuat ulang setiap komit dan menghilangkan overhead dalam menentukan waktu modifikasi file.
x-yuri
@ x-yori inilah mengapa saya memilih untuk menyimpan bidang yang diperbarui ini di database daripada menjalankan filemtime stackoverflow.com/questions/56588850/…
drooh
9

gunakan Class = "NO-CACHE"

contoh 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();
}

Hasil: src = "images / img1.jpg" => src = "images / img1.jpg? A = 0.08749723793963926"

Aref Rostamkhani
sumber
Sederhana, elegan, dan tidak memerlukan rendering sisi server. BAGUS!
Ahi Tuna
7

Anda dapat menulis skrip proxy untuk menyajikan gambar - itu sedikit lebih berhasil. Sesuatu seperti ini:

HTML:

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

Naskah:

// 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();
}

Sebenarnya baik header tanpa-cache atau nomor acak pada gambar src seharusnya cukup, tetapi karena kita ingin menjadi bukti peluru ..

Mindeh
sumber
Solusi Anda bagus, kecuali Pragma bukan header respons.
Piskvor meninggalkan gedung
1
@Piskvor: w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32 sepertinya mengatakan sebaliknya.
Grizly
6

Saya seorang Coder BARU, tapi inilah yang saya temukan, untuk menghentikan Browser dari caching dan menahan tampilan webcam saya:

<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">

Tidak yakin apa yang berfungsi pada Browser apa, tetapi berfungsi untuk beberapa: IE: Berfungsi saat halaman web di-refresh dan ketika situs web dikunjungi kembali (tanpa penyegaran). CHROME: Hanya berfungsi saat halaman web di-refresh (bahkan setelah dikunjungi kembali). SAFARI dan iPad: Tidak berfungsi, saya harus menghapus Riwayat & Data Web.

Ada Ide tentang SAFARI / iPad?

Timmy T.
sumber
4

Saat mengunggah gambar, nama filenya tidak disimpan di database. Ini berganti nama menjadi Image.jpg (untuk memudahkan saat menggunakannya).

Ubah ini, dan Anda telah memperbaiki masalah Anda. Saya menggunakan stempel waktu, seperti solusi yang diusulkan di atas: Image- <timestamp> .jpg

Agaknya, masalah apa pun yang Anda hindari dengan menjaga nama file yang sama untuk gambar tersebut dapat diatasi, tetapi Anda tidak mengatakan apa itu.

AmbroseChapel
sumber
4

Saya memeriksa semua jawaban di seluruh web dan yang terbaik sepertinya: (sebenarnya tidak)

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

pertama.

Namun, jika Anda menambahkan parameter cache = none (yang merupakan kata "tidak ada" statis), itu tidak berpengaruh apa-apa, browser masih memuat dari cache.

Solusi untuk masalah ini adalah:

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

di mana Anda pada dasarnya menambahkan stempel waktu unix untuk membuat parameter dinamis dan tidak ada cache, itu berhasil.

Namun, masalah saya sedikit berbeda: Saya memuat gambar bagan php dengan cepat, dan mengontrol halaman dengan parameter $ _GET. Saya ingin gambar dibaca dari cache ketika parameter URL GET tetap sama, dan tidak cache ketika parameter GET berubah.

Untuk mengatasi masalah ini, saya perlu hash $ _GET tetapi karena itu adalah array, inilah solusinya:

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

Edit :

Meskipun solusi di atas berfungsi dengan baik, terkadang Anda ingin menyajikan versi yang di-cache SAMPAI file diubah. (dengan solusi di atas, itu menonaktifkan cache untuk gambar itu sepenuhnya) Jadi, untuk menyajikan gambar yang di-cache dari browser HINGGA ada perubahan dalam penggunaan file gambar:

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

filemtime () mendapat waktu modifikasi file.

Tarik
sumber
2
The filemtimesolusi adalah lebih baik juga karena md5membutuhkan banyak kekuatan pemrosesan.
oriadam
2
Menghabiskan berhari-hari mencoba mendapatkan aplikasi berbasis Chromium untuk berhenti menyimpan gambar dalam cache. ? Nocache dengan waktu gema memecahkan masalah. Terima kasih!
Woody
2

Masalah Anda adalah bahwa meskipun ada Expires:header, browser Anda menggunakan kembali salinan gambar dalam memori dari sebelum diperbarui, daripada memeriksa cache-nya.

Saya mengalami situasi yang sangat mirip saat mengunggah gambar produk di backend admin untuk situs seperti toko, dan dalam kasus saya, saya memutuskan opsi terbaik adalah menggunakan javascript untuk memaksa penyegaran gambar, tanpa menggunakan teknik modifikasi URL apa pun orang lain telah disebutkan di sini. Sebaliknya, saya menempatkan URL gambar ke dalam IFRAME tersembunyi, bernamalocation.reload(true) di jendela IFRAME, dan kemudian mengganti gambar saya di halaman tersebut. Ini memaksa penyegaran gambar, tidak hanya di halaman yang saya buka, tetapi juga di halaman berikutnya yang saya kunjungi - tanpa klien atau server harus mengingat parameter URL querystring atau fragmen.

Saya memposting beberapa kode untuk melakukan ini dalam jawaban saya di sini .

Doin
sumber
2

Tambahkan cap waktu <img src="picture.jpg?t=<?php echo time();?>">

akan selalu memberi file Anda nomor acak di bagian akhir dan menghentikan cache

BritishSam
sumber
Perlu dicatat bahwa mengimplementasikan solusi ini memberi tekanan pada server per permintaan dan tidak akan selalu berfungsi karena browser dapat menyimpan halaman php yang berisi waktu yang dihasilkan pada saat permintaan awal yang di-cache. Ini hanya akan bekerja dengan andal jika halaman juga memiliki string kueri dinamis.
Dmitry
1

Dengan potensi proxy transparan yang berperilaku buruk di antara Anda dan klien, satu-satunya cara untuk sepenuhnya menjamin bahwa gambar tidak akan disimpan dalam cache adalah dengan memberinya uri unik, seperti memberi tag pada stempel waktu sebagai string kueri atau sebagai bagian dari jalan.

Jika stempel waktu tersebut sesuai dengan waktu pembaruan terakhir gambar, Anda dapat menyimpan ke cache saat diperlukan dan menampilkan gambar baru di saat yang tepat.

pengguna7375
sumber
1

Saya berasumsi pertanyaan asli tentang gambar yang disimpan dengan beberapa info teks. Jadi, jika Anda memiliki akses ke konteks teks saat membuat src = ... url, pertimbangkan untuk menyimpan / menggunakan CRC32 byte gambar alih-alih stempel waktu atau acak yang tidak berarti. Kemudian, jika halaman dengan banyak gambar ditampilkan, hanya gambar yang diperbarui yang akan dimuat ulang. Akhirnya, jika penyimpanan CRC tidak memungkinkan, itu dapat dihitung dan ditambahkan ke url saat runtime.

jbw
sumber
Atau, gunakan waktu modifikasi terakhir gambar, alih-alih CRC, bahkan lebih baik!
Lakukan
1

Dari sudut pandang saya, menonaktifkan cache gambar adalah ide yang buruk. Sama sekali.

Akar masalah di sini adalah - bagaimana memaksa browser untuk memperbarui gambar, ketika telah diperbarui di sisi server.

Sekali lagi, dari sudut pandang pribadi saya, solusi terbaik adalah menonaktifkan akses langsung ke gambar. Sebagai gantinya, akses gambar melalui filter sisi server / servlet / alat / layanan serupa lainnya.

Dalam kasus saya ini adalah layanan istirahat, yang mengembalikan gambar dan melampirkan ETag sebagai tanggapan. Layanan menyimpan hash dari semua file, jika file diubah, hash diperbarui. Ini berfungsi dengan sempurna di semua browser modern. Ya, memang butuh waktu untuk mengimplementasikannya, tetapi itu sangat berharga.

Satu-satunya pengecualian - adalah favicon. Untuk beberapa alasan, itu tidak berhasil. Saya tidak bisa memaksa browser untuk memperbarui cache-nya dari sisi server. ETags, Cache Control, Expires, Pragma headers, tidak ada yang membantu.

Dalam kasus ini, menambahkan beberapa parameter random / versi ke dalam url, tampaknya, adalah satu-satunya solusi.

Alexandr
sumber
1

Idealnya, Anda harus menambahkan tombol / keybinding / menu ke setiap halaman web dengan opsi untuk menyinkronkan konten.

Untuk melakukannya, Anda harus melacak sumber daya yang mungkin perlu disinkronkan, dan menggunakan xhr untuk menyelidiki gambar dengan string kueri dinamis, atau membuat gambar pada waktu proses dengan src menggunakan string kueri dinamis. Kemudian gunakan mekanisme penyiaran untuk memberi tahu semua komponen halaman web yang menggunakan sumber daya untuk memperbarui untuk menggunakan sumber daya dengan querystring dinamis yang ditambahkan ke url-nya.

Contoh yang naif terlihat seperti ini:

Biasanya, gambar ditampilkan dan di-cache, tetapi jika pengguna menekan tombol, permintaan xhr dikirim ke sumber daya dengan menambahkan string kueri waktu ke sana; karena waktu dapat diasumsikan berbeda pada setiap pers, ini akan memastikan bahwa browser akan melewati cache karena tidak dapat mengetahui apakah sumber daya dibuat secara dinamis di sisi server berdasarkan kueri, atau apakah itu statis sumber daya yang mengabaikan kueri.

Hasilnya adalah Anda dapat menghindari semua pengguna membombardir Anda dengan permintaan sumber daya sepanjang waktu, tetapi pada saat yang sama, izinkan mekanisme bagi pengguna untuk memperbarui sumber daya mereka jika mereka mencurigai bahwa mereka tidak sinkron.

<!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>
Dmitry
sumber
0

Anda harus menggunakan nama file yang unik. Seperti ini

<img src="cars.png?1287361287" alt="">

Tetapi teknik ini berarti penggunaan server yang tinggi dan pemborosan bandwidth. Sebaliknya, Anda harus menggunakan nomor versi atau tanggal. Contoh:

<img src="cars.png?2020-02-18" alt="">

Tapi Anda ingin itu tidak pernah menyajikan gambar dari cache. Untuk ini, jika halaman tidak menggunakan cache halaman , dimungkinkan dengan PHP atau sisi server.

<img src="cars.png?<?php echo time();?>" alt="">

Namun, itu masih belum efektif. Alasan: Cache browser ... Metode terakhir namun paling efektif adalah JAVASCRIPT Asli. Kode sederhana ini menemukan semua gambar dengan kelas "NO-CACHE" dan membuat gambar menjadi unik. Letakkan ini di antara tag skrip.

var items = document.querySelectorAll("img.NO-CACHE");
for (var i = items.length; i--;) {
    var img = items[i];
    img.src = img.src + '?' + Date.now();
}

PEMAKAIAN

<img class="NO-CACHE" src="https://upload.wikimedia.org/wikipedia/commons/6/6a/JavaScript-logo.png" alt="">

HASIL Seperti Ini

https://example.com/image.png?1582018163634
Ali
sumber