Bir web tarayıcısını görüntüleri önbelleğe almamaya zorlama


124

Arka fon

İki pro-bono web sitesi için çok basit bir CGI tabanlı (Perl) içerik yönetim aracı yazıyorum ve kullanıyorum. Web sitesi yöneticisine, alanları (tarih, yer, başlık, açıklama, bağlantılar vb.) Doldurup kaydettikleri etkinlikler için HTML formları sağlar. Bu formda, yöneticinin olayla ilgili bir resim yüklemesine izin veriyorum. Formu görüntüleyen HTML sayfasında, yüklenen resmin (HTML img etiketi) bir önizlemesini de gösteriyorum.

Sorun

Sorun, yönetici resmi değiştirmek istediğinde ortaya çıkar. Sadece "göz at" düğmesine basması, yeni bir resim seçmesi ve tamam'a basması gerekirdi. Ve bu iyi çalışıyor.

Görüntü yüklendikten sonra, arka uç CGI yüklemeyi yönetir ve formu düzgün bir şekilde yeniden yükler.

Sorun gösterilen görüntü olmasıdır gelmez yenilenmesini olsun. Veritabanı doğru resmi tutsa bile eski resim hala gösterilmektedir. Görüntüyü web tarayıcısında önbelleğe alındığına kadar daralttım. Yönetici Firefox / Explorer / Safari'de YENİDEN YÜKLE düğmesine basarsa, her şey iyi bir şekilde yenilenir ve yeni görüntü belirir.

Çözümüm - Çalışmıyor

Önbelleği, geçmişte çok uzak bir tarihe sahip bir HTTP Expires talimatı yazarak kontrol etmeye çalışıyorum.

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

Ben idari tarafta olduğumu ve sayfaların her zaman süresi dolduğu için yüklenmesinin biraz daha uzun sürmesini gerçekten umursamadığımı unutmayın.

Ancak bu da çalışmıyor.

notlar

Bir görüntüyü karşıya yüklerken, dosya adı veri tabanında tutulmaz. Image.jpg olarak yeniden adlandırılmıştır (onu kullanırken basitçe bir şeyler ortaya çıkarmak için). Mevcut görüntüyü yenisiyle değiştirirken isim de değişmez. Sadece görüntü dosyasının içeriği değişir.

Web sunucusu, barındırma hizmeti / ISS tarafından sağlanır. Apache kullanır.

Soru

Web tarayıcısını resimleri değil, bu sayfadaki şeyleri önbelleğe almamaya zorlamanın bir yolu var mı?

Veritabanıyla birlikte "dosya adını kaydetme" seçeneğiyle hokkabazlık yapıyorum. Bu şekilde, görüntü değiştirilirse, IMG etiketinin src'si de değişecektir. Ancak, bu site genelinde çok fazla değişiklik gerektiriyor ve daha iyi bir çözümüm varsa bunu yapmamayı tercih ediyorum. Ayrıca, yüklenen yeni görüntü aynı ada sahipse bu yine de çalışmayacaktır (örneğin görüntünün biraz photoshop yapıldığını ve yeniden yüklendiğini söyleyin).


Sanırım bu soru, bir süre önce yazdığım zaman oraya koyduğum "şimdi işe yaramaz bağlamı" ortadan kaldırmak için yeniden işlenebilir. Bence başlık doğru ve en iyi cevap. Ancak soru aslında pek çok türde yanıt için çok yer sağlamak için yazılmıştır ve bu kadar basit bir çözüm beklenemezdi. Bu nedenle, cevabı almak için oraya gelen insanlar için soru biraz karmaşıktır.
Philibert Perusse

Yanıtlar:


186

Armin Ronacher doğru fikre sahip. Sorun, rastgele dizelerin çarpışabilmesidir. Kullanmak istiyorum:

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

"1222259157.415" sunucudaki geçerli saattir. Python ile veya
ile Javascript ile zaman üretperformance.now()time.time()


32
Önemli bir ek, bir tarayıcıyı hiçbir zaman bir şey yapmaya zorlayamayacağınızdır . Yapabileceğiniz tek şey dostça önerilerde bulunmaktır. Bu önerileri fiilen takip etmek tarayıcıya ve kullanıcıya bağlıdır. Bir tarayıcı bunu göz ardı etmekte serbesttir veya bir kullanıcı varsayılanları geçersiz kılabilir.
Joel Coehoorn

8
Joel, bunu kendi cevabına eklesen daha iyi olur.
epochwolf

1
Sadece bir düşünce ve buradaki partiye gelmek için çok geç - ancak görüntünün değiştirilmesinin kontrolüne sahip olduğunuz için, bir sorgu dizisi eklemek yerine, görüntüyü güncellendikçe yeniden adlandırmak belki daha iyi olur. Örneğin: 1222259157.jpg, resim.jpg yerine 1222259157. Bu şekilde güncellenir ve yeniden ziyaret edildiğinde yeniden önbelleğe alınır.
danjah

43
Önbelleğe almayı tamamen engelleyecek olan sunucu saatini eklemek yerine, neden dosyanın son değiştirilme zamanını '?' İşaretinden sonra eklemiyorsunuz. Bu şekilde, resim bir sonraki değişikliğe kadar normal olarak önbelleğe alınacaktır.
Doin

3
Doin'in son yorumu olumlu oylanmalı! Akıllı önbelleğe alma gerçekten önemlidir, neden birisinin aynı dosyayı defalarca indirmesi gerekir?
f

46

Basit çözüm: Resme rastgele bir sorgu dizesi ekleyin:

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

HTTP RFC ne diyor:

Cache-Control: no-cache

Ama bu o kadar iyi çalışmıyor :)


1
Cache-Control: no-cache normal html <img> etiketinde nasıl verilir?
P Satish Patro

Yanıt başlığında gelmesi gerekiyor mu?
P Satish Patro

22

Kullandığım PHP'nin dosya modifiye zaman fonksiyonu örneğin:

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

Görüntüyü değiştirirseniz, değiştirilmiş farklı bir zaman damgasına sahip olduğu için önbelleğe alınan görüntü yerine yeni görüntü kullanılır.


Harika bir ipucu. Değiştirilme zamanı değişirse (üzerine yazılır veya değiştirilirse) hem önbelleğe alma hem de görüntü istemciye yeniden gönderilir.
Vasilis Lourdas

başka bir seçenek için buraya bakın stackoverflow.com/questions/56588850/…
drooh

Mutlak başardı. Harika bahşiş dostum.
Khurram Shaikh

Mükemmel ! Ekstra bir ipucu: <img src = "images / image.png? <? Php echo filemtime ('images / image.jpg')?>">
Rica Gurgel

16

Kullanmak istiyorum:

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

burada "20130910043254", dosyanın değişiklik zamanıdır.

Bir görüntüyü yüklerken, dosya adı veritabanında tutulmaz. Image.jpg olarak yeniden adlandırılmıştır (onu kullanırken basitçe bir şeyler ortaya çıkarmak için). Mevcut görüntüyü yenisiyle değiştirirken isim de değişmez. Sadece görüntü dosyasının içeriği değişir.

Bence iki tür basit çözüm var: 1) ilk akla gelenler (basit çözümler, çünkü bulmaları kolay), 2) bir şeyleri baştan düşündükten sonra bulduklarınız (çünkü bunlar kolay kullanımı). Görünüşe göre, her şeyi yeniden düşünmeyi seçersen her zaman fayda sağlamayacaksın. Ancak ikinci seçeneklerin oldukça hafife alındığına inanıyorum. Sadece neden phpbu kadar popüler olduğunu bir düşünün ;)


1
+1 Sanırım bu diğer cevaplardan çok daha iyi bir fikir çünkü lazer değiştirme süresini kullanmak, görselde hiçbir şey değişmediğinde sunucunuz aynı görüntüyü aynı tarayıcıya birden çok kez vermeye çalışmayacaktır. Her istek olduğunda görüntünün son yazma süresini çıkarmak için biraz ek yük vardır, ancak büyük bir görüntü için, her seferinde görüntüyü döndürmek zorunda kalmaktan daha iyidir ve görüntü değiştiğinde, kullanıcı yenisine sahip olacaktır. Bu güzel küçük ekleme için teşekkür ederim.
Samuel

Eh, değişiklik zamanı ve görüntü yolu veritabanında saklanabilir. Dolayısıyla, ek yük daha da az önemli olabilir. Öte yandan, bahsettiğimiz kaynak kodun bir parçası olan görüntüler ise, değişiklik zamanlarını da önbelleğe alabilir. Görüntülerin değişiklik sürelerine sahip bir komut dosyası oluşturarak (örneğin images.php). Bu komut dosyası, her işlemede yeniden oluşturulmalıdır ve dosyaların değişiklik zamanlarını belirleme yükünü ortadan kaldırır.
x-yuri

@ x-yori işte bu yüzden bu güncellenmiş alanı filemtime stackoverflow.com/questions/56588850/…
drooh

9

Kullanım Sınıf = "no-cache"

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

Sonuç: src = "resimler / img1.jpg" => src = "resimler / img1.jpg? A = 0.08749723793963926"


Basit, zarif ve sunucu tarafında oluşturma gerektirmez. GÜZEL!
Ahi Tuna

7

Görüntüleri sunmak için bir proxy komut dosyası yazabilirsiniz - bu biraz daha fazla çalışma gerektiriyor. Şuna benzer bir şey:

HTML:

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

Senaryo:

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

Aslında ya önbelleksiz üstbilgiler ya da image src'deki rastgele sayılar yeterli olmalı, ancak kurşun geçirmez olmak istediğimiz için ..


Pragma'nın bir yanıt başlığı olmaması dışında sizinki iyi bir çözüm.
Piskvor,

1
@Piskvor: w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32 aksini söylüyor gibi görünüyor.
Grizly

6

Ben YENİ bir Kodlayıcıyım, ancak Tarayıcının önbelleğe almasını ve web kamerası görünümlerimi tutmasını engellemek için şunu buldum:

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

Hangi Tarayıcıda neyin işe yaradığından emin değilim, ancak bazıları için çalışıyor: IE: Web sayfası yenilendiğinde ve web sitesi yeniden ziyaret edildiğinde (yenileme olmadan) çalışır. CHROME: Yalnızca web sayfası yenilendiğinde çalışır (bir yeniden ziyaretten sonra bile). SAFARI ve iPad: Çalışmıyor, Geçmişi ve Web Verilerini temizlemem gerekiyor.

SAFARI / iPad hakkında herhangi bir fikriniz var mı?


4

Bir görüntüyü yüklerken, dosya adı veritabanında tutulmaz. Image.jpg olarak yeniden adlandırılmıştır (onu kullanırken basitçe bir şeyler ortaya çıkarmak için).

Bunu değiştirin ve sorununuzu çözdünüz. Yukarıda önerilen çözümlerde olduğu gibi zaman damgaları kullanıyorum: Image- <timestamp> .jpg

Muhtemelen, görüntü için aynı dosya adını koruyarak kaçındığınız sorunların üstesinden gelinebilir, ancak ne olduklarını söylemiyorsunuz.


4

İnternetteki tüm cevapları kontrol ettim ve en iyisi şöyle görünüyordu: (aslında değil)

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

Başta.

Ancak, önbellek = yok eklerseniz parametresini (statik "yok" kelimesi olan) , hiçbir şeyi etkilemez, tarayıcı yine de önbellekten yükler.

Bu sorunun çözümü şuydu:

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

Parametreyi dinamik yapmak ve önbellek olmadan temelde unix zaman damgası eklediğiniz yerde işe yaradı.

Ancak benim sorunum biraz farklıydı: Anında oluşturulan php grafik görüntüsünü yüklüyordum ve sayfayı $ _GET parametreleriyle kontrol ediyordum. URL GET parametresi aynı kaldığında görüntünün önbellekten okunmasını ve GET parametreleri değiştiğinde önbelleğe alınmamasını istedim.

Bu sorunu çözmek için $ _GET hash'lamam gerekiyordu, ancak dizi olduğu için çözüm burada:

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

Düzenle :

Yukarıdaki çözüm gayet iyi çalışsa da, bazen dosya değişene KADAR önbelleğe alınmış sürümü sunmak istersiniz. (yukarıdaki çözümle, o görüntünün önbelleğini tamamen devre dışı bırakır) Dolayısıyla, tarayıcıdan önbelleğe alınmış görüntüyü sunmak için görüntü dosyası kullanımında bir değişiklik OLANA KADAR:

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

filemtime () dosya değiştirme zamanını alır.


2
filemtimeSolüsyon daha da çünkü md5işlem gücü çok zaman alır.
oriadam

2
Görüntüleri önbelleğe almayı durdurmak için Chromium tabanlı uygulamayı almaya çalışırken günler geçirdim. Zaman yankılı nocache sorunu çözdü. Teşekkür ederim!
Woody

2

Sizin sorununuz, Expires:başlığa rağmen tarayıcınızın önbelleğini kontrol etmek yerine, görüntünün güncellenmeden önceki bellek içi kopyasını yeniden kullanıyor olmasıdır.

Mağaza benzeri bir site için yönetici arka ucuna ürün resimlerini yüklerken çok benzer bir durum yaşadım ve benim durumumda en iyi seçeneğin, diğer kişilerin URL değiştirme tekniklerinden herhangi birini kullanmadan bir resmi yenilemeye zorlamak için javascript kullanmak olduğuna karar verdim. burada daha önce bahsetti. Bunun yerine, görsel URL'sini location.reload(true)IFRAME'in penceresinde çağrılan gizli bir IFRAME içine koydum ve ardından sayfadaki resmimi değiştirdim. Bu, yalnızca bulunduğum sayfada değil, aynı zamanda ziyaret ettiğim sonraki sayfalarda da görüntünün yenilenmesini zorlar - istemci veya sunucu herhangi bir URL sorgu dizesini veya parça tanımlayıcı parametresini hatırlamak zorunda kalmadan.

Buradaki cevabımda bunu yapmak için bazı kodlar gönderdim .


2

Zaman damgası ekleyin <img src="picture.jpg?t=<?php echo time();?>">

dosyanızın sonunda her zaman rastgele bir sayı verir ve önbelleğe almayı durdurur


Bu çözümü uygulamanın, istek başına sunucuya baskı uyguladığına ve tarayıcı, önbelleğe alınan ilk istek sırasında oluşturulan zamanı içeren php sayfasını önbelleğe alabildiğinden, işe yaramayacağına dikkat etmek gerekir. Bu, yalnızca sayfanın dinamik bir sorgu dizesine sahip olması durumunda güvenilir şekilde çalışır.
Dmitry

1

Sizinle müşteri arasında kötü davranan şeffaf proxy'ler potansiyeli varken, görüntülerin önbelleğe alınmayacağını tamamen garanti etmenin tek yolu onlara benzersiz bir uri vermektir, bir zaman damgasını bir sorgu dizesi olarak veya bir parçası olarak etiketlemek gibi bir şey yolu.

Bu zaman damgası görüntünün son güncelleme zamanına karşılık geliyorsa, yeni görüntüyü tam zamanında sunmanız ve sunmanız gerektiğinde önbelleğe alabilirsiniz.


1

Orijinal sorunun, bazı metin bilgileriyle depolanan görüntülerle ilgili olduğunu varsayıyorum. Bu nedenle, src = ... url oluştururken bir metin bağlamına erişiminiz varsa, anlamsız rastgele veya zaman damgası yerine CRC32 görüntü baytlarını depolamayı / kullanmayı düşünün. Ardından, bol miktarda görsel içeren sayfa görüntüleniyorsa, yalnızca güncellenmiş resimler yeniden yüklenecektir. Sonunda, CRC depolaması imkansızsa, çalışma zamanında hesaplanabilir ve url'ye eklenebilir.


Veya bir CRC yerine görüntünün son değiştirilme zamanını kullanın, daha da iyisi!
Doin

1

Benim açımdan, görüntüleri önbelleğe almayı devre dışı bırakmak kötü bir fikirdir. Hiç.

Buradaki temel sorun, tarayıcının bir sunucu tarafında güncellendiğinde görüntüyü güncellemeye nasıl zorlanacağıdır.

Yine, kişisel bakış açıma göre, en iyi çözüm resimlere doğrudan erişimi devre dışı bırakmaktır. Bunun yerine, görüntülere sunucu tarafı filtre / servlet / diğer benzer araçlar / hizmetler aracılığıyla erişin.

Benim durumumda, görüntüyü döndüren ve yanıt olarak ETag ekleyen bir dinlenme servisi. Hizmet tüm dosyaların karmasını tutar, dosya değiştirilirse karma güncellenir. Tüm modern tarayıcılarda mükemmel çalışır. Evet, uygulanması zaman alıyor ama buna değer.

Tek istisna - faviconlardır. Bazı nedenlerden dolayı çalışmıyor. Tarayıcıyı önbelleğini sunucu tarafından güncellemeye zorlayamadım. ETag'ler, Cache Control, Expires, Pragma başlıkları, hiçbir şey yardımcı olmadı.

Bu durumda, url'ye rastgele / sürüm parametresi eklemek, görünen o ki, tek çözüm.


1

İdeal olarak, içeriği senkronize etme seçeneği ile her web sayfasına bir düğme / tuş bağlama / menü eklemelisiniz.

Bunu yapmak için, senkronize edilmesi gerekebilecek kaynakları takip edersiniz ve görüntüleri dinamik bir sorgu dizesiyle incelemek için xhr kullanırsınız veya dinamik sorgu dizesi kullanarak src ile çalışma zamanında bir görüntü oluşturabilirsiniz. Ardından, kaynağı URL'sine eklenen dinamik bir sorgu dizesiyle kullanmak üzere güncellemek için kaynağı kullanan web sayfalarının tüm bileşenlerini bilgilendirmek için bir yayın mekanizması kullanın.

Saf bir örnek şuna benzer:

Normalde, görüntü görüntülenir ve önbelleğe alınır, ancak kullanıcı düğmeye basarsa, kaynağa bir zaman sorgu dizesi eklenmiş bir xhr isteği gönderilir; her basışta zamanın farklı olduğu varsayılabildiğinden, kaynağın sorguya göre sunucu tarafında dinamik olarak üretilip oluşturulmadığını veya statik olup olmadığını anlayamadığından tarayıcının önbelleği atlamasını sağlar. sorguyu yok sayan kaynak.

Sonuç olarak, tüm kullanıcılarınızın sizi her zaman kaynak istekleriyle bombardıman etmesini önleyebilirsiniz, ancak aynı zamanda, kullanıcıların senkronize olmadıklarından şüphelenmeleri halinde kaynaklarını güncellemeleri için bir mekanizmaya izin verin.

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

Benzersiz bir dosya adı kullanmalısınız. Bunun gibi

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

Ancak bu teknik, yüksek sunucu kullanımı ve bant genişliği israfı anlamına gelir. Bunun yerine sürüm numarasını veya tarihi kullanmalısınız. Misal:

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

Ancak hiçbir zaman önbellekten görüntü sunmamasını istiyorsunuz. Bunun için sayfa sayfa önbelleği kullanmıyorsa PHP veya sunucu tarafı ile mümkündür.

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

Ancak yine de etkili değil. Sebep: Tarayıcı önbelleği ... Son fakat en etkili yöntem Yerel JAVASCRIPT'dir. Bu basit kod , "NO-CACHE" sınıfına sahip tüm görüntüleri bulur ve görüntüleri neredeyse benzersiz kılar. Bunu komut dosyası etiketleri arasına yerleştirin.

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

KULLANIM

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

SONUÇLAR Buna Benzer

https://example.com/image.png?1582018163634
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.