Pytanie w sprawie image, javascript, refresh, url – Odśwież obraz za pomocą nowego w tym samym adresie URL

285

Uzyskuję dostęp do linku w mojej witrynie, który zapewni nowy obraz za każdym razem, gdy będzie dostępny.

Problem, z którym się spotykam, polega na tym, że jeśli próbuję załadować obraz w tle, a następnie zaktualizować obraz na stronie, obraz się nie zmienia - mimo że jest aktualizowany po ponownym załadowaniu strony.

var newImage = new Image();
newImage.src = "http://localhost/image.jpg";

function updateImage()
{
if(newImage.complete) {
    document.getElementById("theText").src = newImage.src;
    newImage = new Image();
    number++;
    newImage.src = "http://localhost/image/id/image.jpg?time=" + new Date();
}

    setTimeout(updateImage, 1000);
}

Nagłówki, jak je widzi FireFox:

HTTP/1.x 200 OK
Cache-Control: no-cache, must-revalidate
Pragma: no-cache
Transfer-Encoding: chunked
Content-Type: image/jpeg
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Server: Microsoft-HTTPAPI/1.0
Date: Thu, 02 Jul 2009 23:06:04 GMT

Muszę wymusić odświeżenie tylko tego obrazu na stronie. Jakieś pomysły?

Twoja odpowiedź

16   odpowiedzi
1

Poniższy kod jest przydatny do odświeżania obrazu po kliknięciu przycisku.

function reloadImage(imageId) {
   imgName = 'vishnu.jpg'; //for example
   imageObject = document.getElementById(imageId);
   imageObject.src = imgName;
}

<img src='vishnu.jpg' id='myimage' />

<input type='button' onclick="reloadImage('myimage')" />
305

Spróbuj dodać cachebreaker na końcu adresu URL:

newImage.src = "http://localhost/image.jpg?" + new Date().getTime();

Spowoduje to automatyczne dodanie bieżącego znacznika czasu podczas tworzenia obrazu, a przeglądarka ponownie sprawdzi obraz zamiast pobierać obraz z pamięci podręcznej.

Dziękuję @ Paolo Bergantino za tę odpowiedź. Tracę mój trzy dni na oczyszczenie obrazu Coder
Dlaczego nieMath.random() Gowtham Gopalakrishnan
Czy można z tego korzystać w Amazon S3? Bruno Peres
Ponadto ponowne wyświetlenie tego samego obrazu w innym miejscu, bez „wyłącznika pamięci podręcznej” później, nadal pokazuje zarówno starą wersję z pamięci podręcznej (przynajmniej w Firefoksie)? i # :( T4NK3R
0
<img src='someurl.com/someimage.ext' onload='imageRefresh(this, 1000);'>

Następnie poniżej w niektórych JavaScript

<script language='javascript'>
 function imageRefresh(img, timeout) {
    setTimeout(function() {
     var d = new Date;
     var http = img.src;
     if (http.indexOf("&d=") != -1) { http = http.split("&d=")[0]; } 

     img.src = http + '&d=' + d.getTime();
    }, timeout);
  }
</script>

I tak to się dzieje, gdy obraz ładuje się, planuje go ponownie załadować w 1 sekundę. Używam tego na stronie z kamerami bezpieczeństwa w domu różnego typu.

2

Skończyło się na tym, że serwer mapował każde żądanie obrazu w tym katalogu do źródła, które próbowałem zaktualizować. Następnie mój timer dodał numer na końcu nazwy, aby DOM mógł go zobaczyć jako nowy obraz i załadować go.

Na przykład.

http://localhost/image.jpg
//and
http://localhost/image01.jpg

zażąda tego samego kodu generowania obrazu, ale będzie wyglądał jak inne obrazy w przeglądarce.

var newImage = new Image();
newImage.src = "http://localhost/image.jpg";
var count = 0;
function updateImage()
{
    if(newImage.complete) {
        document.getElementById("theText").src = newImage.src;
        newImage = new Image();
        newImage.src = "http://localhost/image/id/image" + count++ + ".jpg";
    }
    setTimeout(updateImage, 1000);
}
Miałby ten sam problem z buforowaniem wielu kopii obrazu jako rozwiązanie kwerendy (Paolo i kilka innych) i wymaga zmian serwera. TomG
3

function reloadImage(imageId)
{
   path = '../showImage.php?cache='; //for example
   imageObject = document.getElementById(imageId);
   imageObject.src = path + (new Date()).getTime();
}
<img src='../showImage.php' id='myimage' />

<br/>

<input type='button' onclick="reloadImage('myimage')" />

zmianapath='../showImage.php'; dopath='../showImage.php?'; BOOMik
proszę wyjaśnić OP, w jaki sposób i dlaczego to pomaga, zamiast po prostu wklejać kod nomistic
Nie sądzę../showImage.phpFri May 01 2015 17:34:18 GMT+0200 (Mitteleuropäische Sommerzeit) to poprawna nazwa pliku ... Przynajmniej to próbuje załadować ... ByteHamster
6

Czy po utworzeniu nowego obrazu usuwasz stary obraz z DOM i zastępujesz go nowym?

Możesz przechwytywać nowe obrazy przy każdym wywołaniu updateImage, ale nie dodawać ich do strony.

Jest na to wiele sposobów. Coś takiego będzie działać.

function updateImage()
{
    var image = document.getElementById("theText");
    if(image.complete) {
        var new_image = new Image();
        //set up the new image
        new_image.id = "theText";
        new_image.src = image.src;           
        // insert new image and remove old
        image.parentNode.insertBefore(new_image,image);
        image.parentNode.removeChild(image);
    }

    setTimeout(updateImage, 1000);
}

Po uzyskaniu tej pracy, jeśli nadal występują problemy, prawdopodobnie jest to problem z buforowaniem, o którym mówią inne odpowiedzi.

0

Oto moje rozwiązanie. To jest bardzo proste. Harmonogram ramek może być lepszy.

<!doctype html>
<html>
    <head>
        <meta charset="utf-8">      
        <title>Image Refresh</title>
    </head>

    <body>

    <!-- Get the initial image. -->
    <img id="frame" src="frame.jpg">

    <script>        
        // Use an off-screen image to load the next frame.
        var img = new Image();

        // When it is loaded...
        img.addEventListener("load", function() {

            // Set the on-screen image to the same source. This should be instant because
            // it is already loaded.
            document.getElementById("frame").src = img.src;

            // Schedule loading the next frame.
            setTimeout(function() {
                img.src = "frame.jpg?" + (new Date).getTime();
            }, 1000/15); // 15 FPS (more or less)
        })

        // Start the loading process.
        img.src = "frame.jpg?" + (new Date).getTime();
    </script>
    </body>
</html>
4

Jedną z odpowiedzi jest hackishly dodać trochę get get parametr zapytania, jak sugerowano.

Lepszą odpowiedzią jest wyemitowanie kilku dodatkowych opcji w nagłówku HTTP.

Pragma: no-cache
Expires: Fri, 30 Oct 1998 14:19:41 GMT
Cache-Control: no-cache, must-revalidate

Podając datę w przeszłości, nie będzie buforowana przez przeglądarkę.Cache-Control został dodany w HTTP / 1.1, a znacznik must-revalidate wskazuje, że serwery proxy nigdy nie powinny wyświetlać starego obrazu nawet w okolicznościach łagodzących, aPragma: no-cache nie jest tak naprawdę potrzebny w obecnych nowoczesnych przeglądarkach / pamięciach podręcznych, ale może pomóc w niektórych okrutnych, starych, zniszczonych implementacjach.

Właśnie zauważyłem, że odświeżasz ten sam znacznik img w kółko. Przeglądarka prawdopodobnie wykrywa, kiedy ustawiasz src, że src się nie zmieniło, więc nie przejmuje się odświeżaniem. (Ponieważ to sprawdzanie odbywa się na poziomie DOM i nie ma nic wspólnego z celem). Co się stanie, jeśli dodasz „?” + numer - do adresu URL pobieranego obrazu? Edward KMETT
Brzmiało to tak, jakby działało, ale nadal pokazuje ten sam obraz, nawet z hackami. Dodam informacje nagłówka do pytania. QueueHammer
1

Miałem wymaganie: 1) nie mogę dodać żadnego?var=xx do obrazu 2) powinien działać w wielu domenach

Naprawdę lubię opcję # 4ta odpowiedź z jednym ale:

ma problemy z niezawodną pracą z crossdomainem (i wymaga dotknięcia kodu serwera).

Mój szybki i brudny sposób to:

Utwórz ukryty element iframeZaładuj do niego bieżącą stronę (tak, cała strona)iframe.contentWindow.location.reload(true);Ustaw ponownie źródło obrazu na siebie

Oto jest

function RefreshCachedImage() {
    if (window.self !== window.top) return; //prevent recursion
    var $img = $("#MYIMAGE");
    var src = $img.attr("src");
    var iframe = document.createElement("iframe");
    iframe.style.display = "none";
    window.parent.document.body.appendChild(iframe);
    iframe.src = window.location.href;
    setTimeout(function () {
        iframe.contentWindow.location.reload(true);
        setTimeout(function () {
            $img.removeAttr("src").attr("src", src);
        }, 2000);
    }, 2000);
}

Tak, wiem, setTimeout ... Musisz to zmienić na odpowiednie zdarzenia onload.

1

Spróbuj użyć bezwartościowego kwerendy, aby uczynić z niej unikalny adres URL:

function updateImage()
{
    if(newImage.complete) {
        document.getElementById("theText").src = newImage.src;
        newImage = new Image();
        number++;
        newImage.src = "http://localhost/image.jpg?" + new Date();
    }

    setTimeout(updateImage, 1000);
}
Dodano kod WQS do kodu i zweryfikowano, że żądanie jest akceptowane i że przeglądarka widzi odpowiedź jako pochodzącą z adresu + WQS bez odświeżania obrazu. QueueHammer
-2

Użyłem poniższej koncepcji pierwszego powiązania obrazu z fałszywym (buforem) adresem URL, a następnie powiązania go z prawidłowym adresem URL.

imgcover.ImageUrl = ConfigurationManager.AppSettings["profileLargeImgPath"] + "Myapp_CoverPic_" + userid + "Buffer.jpg";

imgcover.ImageUrl = ConfigurationManager.AppSettings["profileLargeImgPath"] + "Myapp_CoverPic_" + userid + ".jpg";

W ten sposób zmuszam przeglądarkę do odświeżenia przy użyciu prawidłowego adresu URL.

2
document.getElementById("img-id").src = document.getElementById("img-id").src

ustaw swój własny src jako src.

188

Widziałem wiele zmian w odpowiedziach, jak to zrobić, więc pomyślałem, że podsumuję je tutaj (plus dodaj czwartą metodę według mojego wynalazku):

(1) Dodaj unikalny parametr zapytania do buforowania pamięci podręcznej do adresu URL, taki jak:
newImage.src = "image.jpg?t=" + new Date().getTime();

Plusy: W 100% niezawodny, szybki i łatwy do zrozumienia i wdrożenia.

Cons: Całkowicie pomija buforowanie, co oznacza niepotrzebne opóźnienia i wykorzystanie przepustowości za każdym razem, gdy obraznie zmiana między widokami. Potencjalnie wypełni pamięć podręczną przeglądarki (i wszelkie pośrednie pamięci podręczne) wieloma, wieloma kopiami dokładnie tego samego obrazu! Ponadto wymaga modyfikacji adresu URL obrazu.

Kiedy użyć: Używaj, gdy obraz stale się zmienia, na przykład w przypadku transmisji z kamery internetowej na żywo. Jeśli używasz tej metody,upewnij się, że sam udostępniasz obrazyCache-control: no-cache Nagłówki HTTP !!! (Często można to skonfigurować za pomocą pliku .htaccess). W przeciwnym razie będziesz stopniowo wypełniać skrytki starymi wersjami obrazu!

(2) Dodaj parametr zapytania do adresu URL, który zmienia się tylko wtedy, gdy plik robi, np .:
echo '<img src="image.jpg?m=' . filemtime('image.jpg') . '">';

(To jest kod po stronie serwera PHP, ale ważnym punktem jest to, że a? M =[czas ostatniej modyfikacji pliku] querystring jest dołączany do nazwy pliku).

Plusy: W 100% niezawodny, szybki i łatwy do zrozumienia i wdrożenia,i zachowuje doskonale zalety buforowania.

Cons: Wymaga modyfikacji adresu URL obrazu. Również trochę więcej pracy dla serwera - musi uzyskać dostęp do czasu ostatniej modyfikacji pliku. Ponadto wymaga informacji po stronie serwera, więc nie nadaje się do rozwiązania wyłącznie po stronie klienta, aby sprawdzić odświeżony obraz.

Kiedy użyć: Gdy chcesz buforować obrazy, ale może być konieczne ich aktualizowanie od czasu do czasu bez zmiany samej nazwy pliku. ORAZ kiedy możesz łatwo zapewnić, że poprawny querystring zostanie dodany do każdej instancji obrazu w HTML.

(3) Służy do wyświetlania obrazów z nagłówkiemCache-control: max-age=0, must-revalidatei dodaj unikalnymemcache-bezpieczny identyfikator fragmentu do adresu URL, taki jak:
newImage.src = "image.jpg#" + new Date().getTime();

Chodzi o to, że nagłówek kontroli pamięci podręcznej umieszcza obrazy w pamięci podręcznej przeglądarki, ale natychmiast zaznacza je jako przestarzałe, więc za każdym razem, gdy są ponownie wyświetlane, przeglądarka musi sprawdzić z serwerem, czy się zmieniły. Zapewnia to, że przeglądarkaPamięć podręczna HTTP zawsze zwraca najnowszą kopię obrazu. Jednak przeglądarki często wykorzystują kopię obrazu w pamięci, jeśli taką mają, i nawet nie sprawdzają w tym przypadku pamięci podręcznej HTTP. Aby temu zapobiec, używany jest identyfikator fragmentu: Porównanie obrazu w pamięcisrczawiera identyfikator fragmentu, ale zostaje usunięty przed wysłaniem zapytania do pamięci podręcznej HTTP. (Tak np.image.jpg#A iimage.jpg#B oba mogą być wyświetlane zimage.jpg wpis w pamięci podręcznej HTTP przeglądarki, aleimage.jpg#B nigdy nie byłby wyświetlany przy użyciu przechowywanych w pamięci danych obrazu od kiedyimage.jpg#A był ostatnio wyświetlany).

Plusy: Właściwie wykorzystuje mechanizmy buforowania HTTP i używa buforowanych obrazów, jeśli się nie zmieniły. Działa na serwerach, które dławią się querystem dodanym do statycznego adresu URL obrazu (ponieważ serwery nigdy nie widzą identyfikatorów fragmentów - są one przeznaczone wyłącznie do użytku przez przeglądarki).

Cons: Opiera się na nieco wątpliwym (lub przynajmniej słabo udokumentowanym) zachowaniu przeglądarek w odniesieniu do obrazów z identyfikatorami fragmentów w ich adresach URL (jednak pomyślnie testowałem to w FF27, Chrome33 i IE11). Nadal wysyła do serwera żądanie rewalidacji dla każdego widoku obrazu, co może być przesadne, jeśli obrazy zmieniają się rzadko i / lub opóźnienie jest dużym problemem (ponieważ trzeba poczekać na odpowiedź rewalidacyjną, nawet jeśli buforowany obraz jest nadal dobry) . Wymaga modyfikacji adresów URL obrazów.

Kiedy użyć: Używaj, gdy obrazy mogą się często zmieniać lub wymagają okresowego odświeżania przez klienta bez udziału skryptu po stronie serwera, ale nadal chcesz mieć zalety buforowania. Na przykład odpytywanie na żywo kamery internetowej, która aktualizuje obraz nieregularnie co kilka minut. Alternatywnie użyj zamiast (1) lub (2), jeśli twój serwer nie zezwala na querystrings na statycznych adresach URL obrazów.

(4) Wymuszone odświeżenie konkretnego obrazu za pomocą Javascript, najpierw ładując go do ukrytego<iframe> a potem dzwonilocation.reload(true) na iframecontentWindow.

Kroki są:

Załaduj obraz, który ma zostać odświeżony, do ukrytego elementu iframe. To jest tylko krok konfiguracji - w razie potrzeby można to zrobić z dużym wyprzedzeniem, jeśli chodzi o odświeżanie. Nie ma nawet znaczenia, czy obraz nie ładuje się na tym etapie!

Po zakończeniu usuń wszystkie kopie tego obrazu na swojej stronie (stronach) lub gdziekolwiek w dowolnym węźle DOM (nawet poza stronami przechowywanymi w zmiennych javascript). Jest to konieczne, ponieważ przeglądarka może w inny sposób wyświetlać obraz ze starej kopii w pamięci (szczególnie IE11): musisz zapewnić wszystkow pamięci kopie są wyczyszczone przed odświeżeniem pamięci podręcznej HTTP. Jeśli inny kod javascript działa asynchronicznie, być może trzeba będzie w międzyczasie zapobiec tworzeniu przez ten kod nowych kopii odświeżanego obrazu.

Połączenieiframe.contentWindow.location.reload(true). Thetrue wymusza obejście pamięci podręcznej, przeładowuje bezpośrednio z serwera i nadpisuje istniejącą kopię w pamięci podręcznej.

Po zakończeniure- ładowanie, przywracanie wygaszonych obrazów. Powinni teraz wyświetlić nową wersję z serwera!

W przypadku obrazów tej samej domeny można bezpośrednio załadować obraz do ramki iframe. W przypadku obrazów międzydomenowych należy zamiast tego załadować stronę HTMLz Twojej domeny który zawiera obraz w<img> tag, w przeciwnym razie otrzymasz błąd „Odmowa dostępu” podczas próby połączeniaiframe.contentWindow.reload(...).

Plusy: Działa tak jak funkcja image.reload ()życzenie DOM miał! Pozwala na normalne buforowanie obrazów (nawet w przypadku przyszłych terminów wygaśnięcia, jeśli chcesz, aby uniknąć częstej rewalidacji). Umożliwia odświeżenie określonego obrazu bez zmiany adresów URL tego obrazu na bieżącej stronie lub na innych stronach, przy użyciu tylko kodu po stronie klienta.

Cons: Opiera się na Javascript. Nie gwarantuję 100% poprawnej pracy w każdej przeglądarce (testowałem to jednak z powodzeniem w FF27, Chrome33 i IE11). Bardzo skomplikowane w stosunku do innych metod.

Kiedy użyć: Gdy masz zbiór zasadniczo statycznych obrazów, które chcesz zbuforować, ale wciąż musisz być w stanie aktualizować je od czasu do czasu i uzyskać natychmiastową wizualną informację zwrotną, że aktualizacja miała miejsce. (Zwłaszcza gdy odświeżenie całej strony przeglądarki nie zadziałałoby, jak w niektórych aplikacjach internetowych zbudowanych na przykład na AJAX). A kiedy metody (1) - (3) nie są wykonalne, ponieważ (z jakiegokolwiek powodu) nie można zmienić wszystkich adresów URL, które mogą wyświetlać obraz, który trzeba zaktualizować. (Zauważ, że używając tych 3 metod obraz zostanie odświeżony, ale jeśliinne strona próbuje następnie wyświetlić ten obrazbez odpowiedni identyfikator kwerendy lub fragmentu, zamiast tego może pokazywać starszą wersję.

Szczegóły wdrożenia tego w sposób rzetelny i elastyczny są podane poniżej:

Załóżmy, że Twoja witryna zawiera pusty plik .gif 1x1 pikseli na ścieżce URL/img/1x1blank.gif, a także posiada następujący jedno-liniowy skrypt PHP (wymagany tylko do zastosowania wymuszonego odświeżania domiędzy domenami obrazy i można je przepisać w dowolnym języku skryptowym po stronie serwera, oczywiście w ścieżce URL/echoimg.php:

<img src="<?=htmlspecialchars(@$_GET['src'],ENT_COMPAT|ENT_HTML5,'UTF-8')?>">

Poniżej przedstawiamy realistyczną implementację tego, co możesz zrobić w Javascript. Wygląda to trochę skomplikowanie, ale jest wiele komentarzy, a ważną funkcją jest po prostu forceImgReload () - dwa pierwsze tylko puste i nie puste obrazy i powinny być zaprojektowane tak, aby wydajnie działały z własnym HTML, więc zakoduj je jako działa najlepiej dla ciebie; wiele komplikacji w nich może być niepotrzebnych dla Twojej witryny:

// This function should blank all images that have a matching src, by changing their src property to /img/1x1blank.gif.
// ##### You should code the actual contents of this function according to your page design, and what images there are on them!!! #####
// Optionally it may return an array (or other collection or data structure) of those images affected.
// This can be used by imgReloadRestore() to restore them later, if that's an efficient way of doing it (otherwise, you don't need to return anything).
// NOTE that the src argument here is just passed on from forceImgReload(), and MAY be a relative URI;
// However, be aware that if you're reading the src property of an <img> DOM object, you'll always get back a fully-qualified URI,
// even if the src attribute was a relative one in the original HTML.  So watch out if trying to compare the two!
// NOTE that if your page design makes it more efficient to obtain (say) an image id or list of ids (of identical images) *first*, and only then get the image src,
// you can pass this id or list data to forceImgReload() along with (or instead of) a src argument: just add an extra or replacement parameter for this information to
// this function, to imgReloadRestore(), to forceImgReload(), and to the anonymous function returned by forceImgReload() (and make it overwrite the earlier parameter variable from forceImgReload() if truthy), as appropriate.
function imgReloadBlank(src)
{
  // ##### Everything here is provisional on the way the pages are designed, and what images they contain; what follows is for example purposes only!
  // ##### For really simple pages containing just a single image that's always the one being refreshed, this function could be as simple as just the one line:
  // ##### document.getElementById("myImage").src = "/img/1x1blank.gif";

  var blankList = [],
      fullSrc = /* Fully qualified (absolute) src - i.e. prepend protocol, server/domain, and path if not present in src */,
      imgs, img, i;

  for each (/* window accessible from this one, i.e. this window, and child frames/iframes, the parent window, anything opened via window.open(), and anything recursively reachable from there */)
  {
    // get list of matching images:
    imgs = theWindow.document.body.getElementsByTagName("img");
    for (i = imgs.length; i--;) if ((img = imgs[i]).src===fullSrc)  // could instead use body.querySelectorAll(), to check both tag name and src attribute, which would probably be more efficient, where supported
    {
      img.src = "/img/1x1blank.gif";  // blank them
      blankList.push(img);            // optionally, save list of blanked images to make restoring easy later on
    }
  }

  for each (/* img DOM node held only by javascript, for example in any image-caching script */) if (img.src===fullSrc)
  {
    img.src = "/img/1x1blank.gif";   // do the same as for on-page images!
    blankList.push(img);
  }

  // ##### If necessary, do something here that tells all accessible windows not to create any *new* images with src===fullSrc, until further notice,
  // ##### (or perhaps to create them initially blank instead and add them to blankList).
  // ##### For example, you might have (say) a global object window.top.blankedSrces as a propery of your topmost window, initially set = {}.  Then you could do:
  // #####
  // #####     var bs = window.top.blankedSrces;
  // #####     if (bs.hasOwnProperty(src)) bs[src]++; else bs[src] = 1;
  // #####
  // ##### And before creating a new image using javascript, you'd first ensure that (blankedSrces.hasOwnProperty(src)) was false...
  // ##### Note that incrementing a counter here rather than just setting a flag allows for the possibility that multiple forced-reloads of the same image are underway at once, or are overlapping.

  return blankList;   // optional - only if using blankList for restoring back the blanked images!  This just gets passed in to imgReloadRestore(), it isn't used otherwise.
}




// This function restores all blanked images, that were blanked out by imgReloadBlank(src) for the matching src argument.
// ##### You should code the actual contents of this function according to your page design, and what images there are on them, as well as how/if images are dimensioned, etc!!! #####
function imgReloadRestore(src,blankList,imgDim,loadError);
{
  // ##### Everything here is provisional on the way the pages are designed, and what images they contain; what follows is for example purposes only!
  // ##### For really simple pages containing just a single image that's always the one being refreshed, this function could be as simple as just the one line:
  // ##### document.getElementById("myImage").src = src;

  // ##### if in imgReloadBlank() you did something to tell all accessible windows not to create any *new* images with src===fullSrc until further notice, retract that setting now!
  // ##### For example, if you used the global object window.top.blankedSrces as described there, then you could do:
  // #####
  // #####     var bs = window.top.blankedSrces;
  // #####     if (bs.hasOwnProperty(src)&&--bs[src]) return; else delete bs[src];  // return here means don't restore until ALL forced reloads complete.

  var i, img, width = imgDim&&imgDim[0], height = imgDim&&imgDim[1];
  if (width) width += "px";
  if (height) height += "px";

  if (loadError) {/* If you want, do something about an image that couldn't load, e.g: src = "/img/brokenImg.jpg"; or alert("Couldn't refresh image from server!"); */}

  // If you saved & returned blankList in imgReloadBlank(), you can just use this to restore:

  for (i = blankList.length; i--;)
  {
    (img = blankList[i]).src = src;
    if (width) img.style.width = width;
    if (height) img.style.height = height;
  }
}




// Force an image to be reloaded from the server, bypassing/refreshing the cache.
// due to limitations of the browser API, this actually requires TWO load attempts - an initial load into a hidden iframe, and then a call to iframe.contentWindow.location.reload(true);
// If image is from a different domain (i.e. cross-domain restrictions are in effect, you must set isCrossDomain = true, or the script will crash!
// imgDim is a 2-element array containing the image x and y dimensions, or it may be omitted or null; it can be used to set a new image size at the same time the image is updated, if applicable.
// if "twostage" is true, the first load will occur immediately, and the return value will be a function
// that takes a boolean parameter (true to proceed with the 2nd load (including the blank-and-reload procedure), false to cancel) and an optional updated imgDim.
// This allows you to do the first load early... for example during an upload (to the server) of the image you want to (then) refresh.
function forceImgReload(src, isCrossDomain, imgDim, twostage)
{
  var blankList, step = 0,                                // step: 0 - started initial load, 1 - wait before proceeding (twostage mode only), 2 - started forced reload, 3 - cancelled
      iframe = window.document.createElement("iframe"),   // Hidden iframe, in which to perform the load+reload.
      loadCallback = function(e)                          // Callback function, called after iframe load+reload completes (or fails).
      {                                                   // Will be called TWICE unless twostage-mode process is cancelled. (Once after load, once after reload).
        if (!step)  // initial load just completed.  Note that it doesn't actually matter if this load succeeded or not!
        {
          if (twostage) step = 1;  // wait for twostage-mode proceed or cancel; don't do anything else just yet
          else { step = 2; blankList = imgReloadBlank(src); iframe.contentWindow.location.reload(true); }  // initiate forced-reload
        }
        else if (step===2)   // forced re-load is done
        {
          imgReloadRestore(src,blankList,imgDim,(e||window.event).type==="error");    // last parameter checks whether loadCallback was called from the "load" or the "error" event.
          if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
        }
      }
  iframe.style.display = "none";
  window.parent.document.body.appendChild(iframe);    // NOTE: if this is done AFTER setting src, Firefox MAY fail to fire the load event!
  iframe.addEventListener("load",loadCallback,false);
  iframe.addEventListener("error",loadCallback,false);
  iframe.src = (isCrossDomain ? "/echoimg.php?src="+encodeURIComponent(src) : src);  // If src is cross-domain, script will crash unless we embed the image in a same-domain html page (using server-side script,)!!!
  return (twostage
    ? function(proceed,dim)
      {
        if (!twostage) return;
        twostage = false;
        if (proceed)
        {
          imgDim = (dim||imgDim);  // overwrite imgDim passed in to forceImgReload() - just in case you know the correct img dimensions now, but didn't when forceImgReload() was called.
          if (step===1) { step = 2; blankList = imgReloadBlank(src); iframe.contentWindow.location.reload(true); }
        }
        else
        {
          step = 3;
          if (iframe.contentWindow.stop) iframe.contentWindow.stop();
          if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
        }
      }
    : null);
}

Następnie, aby wymusić odświeżenie obrazu znajdującego się w tej samej domenie co Twoja strona, możesz po prostu:

forceImgReload("myimage.jpg");

Aby odświeżyć obraz z innego miejsca (między domenami):

forceImgReload("http://someother.server.com/someimage.jpg", true);

Bardziej zaawansowaną aplikacją może być ponowne załadowanie obrazu po przesłaniu nowej wersji na serwer, przygotowanie początkowego etapu procesu przeładowania jednocześnie z przesyłaniem, aby zminimalizować widoczne opóźnienie przeładowania dla użytkownika. Jeśli przesyłasz przez AJAX, a serwer zwraca bardzo prostą tablicę JSON [powodzenie, szerokość, wysokość], to twój kod może wyglądać mniej więcej tak:

// fileForm is a reference to the form that has a the <input typ="file"> on it, for uploading.
// serverURL is the url at which the uploaded image will be accessible from, once uploaded.
// The response from uploadImageToServer.php is a JSON array [success, width, height]. (A boolean and two ints).
function uploadAndRefreshCache(fileForm, serverURL)
{
  var xhr = new XMLHttpRequest(),
      proceedWithImageRefresh = forceImgReload(serverURL, false, null, true);
  xhr.addEventListener("load", function(){ var arr = JSON.parse(xhr.responseText); if (!(arr&&arr[0])) { proceedWithImageRefresh(false); doSomethingOnUploadFailure(...); } else { proceedWithImageRefresh(true,[arr[1],ar[2]]); doSomethingOnUploadSuccess(...); }});
  xhr.addEventListener("error", function(){ proceedWithImageRefresh(false); doSomethingOnUploadError(...); });
  xhr.addEventListener("abort", function(){ proceedWithImageRefresh(false); doSomethingOnUploadAborted(...); });
  // add additional event listener(s) to track upload progress for graphical progress bar, etc...
  xhr.open("post","uploadImageToServer.php");
  xhr.send(new FormData(fileForm));
}

Ostatnia uwaga: chociaż ten temat dotyczy obrazów, potencjalnie dotyczy także innych rodzajów plików lub zasobów. Na przykład, zapobieganie używaniu starych skryptów lub plików css, a nawet odświeżanie zaktualizowanych dokumentów PDF (przy użyciu (4) tylko, jeśli skonfigurowano, aby otworzyć w przeglądarce). W tych przypadkach metoda (4) może wymagać pewnych zmian w powyższym javascript.

Podoba mi się pomysł metody 4, ale nie można załadować zawartości zewnętrznej za pomocą Iframe, prawda? Obecnie używam metody 3 w jednostronicowej aplikacji internetowej, ale nie podoba mi się fakt, że musisz przeładować całą stronę, aby uzyskać nowy obraz, nawet jeśli HTML szablonu zostanie ponownie załadowany. Emilios1995
@Doin dla metody 2, czy nie lepiej, jeśli to zrobimyclearstatcache(true, 'image.jpg'); pierwszy? Czytałem, że PHP buforuje statystyki plików i rozumiem, że nawet jeśli rzeczywisty czas mtime się zmienił, PHP może nadal wysyłać stary mtime.Sok. ADTC
@pseudosavant: Niestety, zauważam to tylko ~ 17 miesięcy później, ale przykro mi, że muszę ci powiedzieć, że twoje zmiany w moim kodzie zostały poważnie zerwane. (Aby być uczciwym, nie sądzę, aby kod wywołania zwrotnego, który miałem początkowo, był poprawny). Przepisałem teraz obszernie część (4), zarówno wyjaśnienie, jak i kod. Twój poprzedni kod nigdy nie wygasał obrazów (więc może się nie udać w dziwny sposób, szczególnie w IE, a zwłaszcza jeśli obraz był wyświetlany w wielu miejscach), ale gorzej też usunął iframe natychmiast po rozpoczęciu pełnego przeładowania, co prawdopodobnie oznaczało, że działają tylko sporadycznie lub wcale. Przepraszam! Doin
@ ADTC Jestem pewien, że PHP buforuje tylko statystyki plikóww ramach indywidualnego skryptuto znaczy, jeśli kod skryptu odczytuje informacje o pliku, a następnie odczytuje je ponownie, druga kopia będzie pochodzić ze statcache. W celu omówionym tutaj prawdopodobnie jest to poprawne zachowanie: jeśli masz dwie lub więcej kopii tego samego obrazu na stronie HTML, wszystkie powinny mieć ten sam adres URL, nawet jeśli obraz jest aktualizowany w połowie skryptu PHP generującego strona. Doin
0

Rozwiązałem ten problem, wysyłając dane z powrotem przez serwlet.

response.setContentType("image/png");
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache, must-revalidate");
response.setDateHeader("Expires", 0);

BufferedImage img = ImageIO.read(new File(imageFileName));

ImageIO.write(img, "png", response.getOutputStream());

Następnie ze strony dajesz mu serwlet z kilkoma parametrami, aby pobrać poprawny plik obrazu.

<img src="YourServlet?imageFileName=imageNum1">
174

Jako alternatywa dla ...

newImage.src = "http://localhost/image.jpg?" + new Date().getTime();

...wygląda na to że...

newImage.src = "http://localhost/image.jpg#" + new Date().getTime();

... jest wystarczające, aby oszukać pamięć podręczną przeglądarki bez pomijania pamięci podręcznych nadrzędnych, zakładając, że zwróciłeś poprawnąCache-Control nagłówki. Chociaż możesz użyć ...

Cache-Control: no-cache, must-revalidate

... tracisz korzyści zIf-Modified-Since lubIf-None-Match nagłówki, więc coś w stylu ...

Cache-Control: max-age=0, must-revalidate

... powinno uniemożliwić przeglądarce ponowne pobranie całego obrazu, jeśli w rzeczywistości się nie zmienił. Przetestowany i działa na IE, Firefox i Chrome. Irytująco nie działa na Safari, chyba że użyjesz ...

Cache-Control: no-store

... mimo że może to być lepsze niż wypełnianie pamięci podręcznych z setkami identycznych obrazów, szczególnie gdy działają na własnym serwerze. ;-)

Aktualizacja (2014-09-28): Obecnie wygląda takCache-Control: no-store jest również potrzebny w Chrome.

Aby uniknąć narzutu na tworzenie obiektów i / lub wywołanie metody, można użyć rosnącej liczby całkowitej jako cache-buster:newImage.src = "http://localhost/image.jpg#" + i++; laindir
Istnieje kilka pamięci podręcznej nagłówka. właściwie nie znam angielskiego zbyt dobrze, możesz mi powiedzieć, czy powinienem użyć które ?! Chcę czegoś, co nie buforuje tylko zmienionego zdjęcia (jak captcha) i buforuje inne rzeczy. więcCache-Control: max-age=0, must-revalidate jest dla mnie dobry? Shafizadeh
Świetny! Po wielu próbach załadowania moich obrazów internetowych, które były ładowane z opóźnieniem, właśnie rozwiązałem je, stosując swoje rozwiązanie (z '#', używanie '?' Nie działa dla mnie). Wielkie dzięki!!! user304602
Tam sąDWA zaangażowane pamięci podręczne: jest regularna pamięć podręczna HTTP przeglądarki i pamięć podręczna obrazów wyświetlanych ostatnio. Ta ostatnia pamięć podręczna jest w pełni indeksowanasrc atrybut, więc dodanie unikalnego identyfikatora fragmentu zapewnia, że ​​obraz nie jest po prostu wyciągany z pamięci. Ale identyfikatory fragmentów nie są wysyłane jako część żądań HTTP, więc zwykła pamięć podręczna HTTP będzie używana normalnie. Dlatego ta technika działa. Doin
To nie działa dla mnie. Jedyną różnicą w moim przypadku jest to, że mam adres URL do akcji kontrolera, która pobiera img z db. Miałem inne argumenty dla akcji kontrolera, więc dodałem ją jako „...... & convert = true & t =” + new Date (). GetTime (); i „...... & convert = true #” + nowy Date (). getTime () ;. Czy jest coś, co robię źle? shaffooo
0

Poniższy przykład, oparty na kodzie Doina nr 4, upraszcza ten kod, wykorzystując go bardzo dobrzedocument.write zamiastsrcwiframe wspierać CORS. Skupia się tylko na usuwaniu pamięci podręcznej przeglądarki, nie przeładowując każdego obrazu na stronie.

Poniżej jest napisanetypescript i używaangular $ q obietnica biblioteki, tylko fyi, ale powinna być łatwa do przeniesienia na waniliowy javascript. Metoda ma żyć w klasie maszynopisów.

Zwraca obietnicę, która zostanie rozwiązana po zakończeniu przeładowywania elementu iframe. Nie testowany, ale działa dobrze dla nas.

    mmForceImgReload(src: string): ng.IPromise<void> {
        var deferred = $q.defer<void>();
        var iframe = window.document.createElement("iframe");

        var firstLoad = true;
        var loadCallback = (e) => {
            if (firstLoad) {
                firstLoad = false;
                iframe.contentWindow.location.reload(true);
            } else {
                if (iframe.parentNode) iframe.parentNode.removeChild(iframe);
                deferred.resolve();
            }
        }
        iframe.style.display = "none";
        window.parent.document.body.appendChild(iframe);
        iframe.addEventListener("load", loadCallback, false);
        iframe.addEventListener("error", loadCallback, false);
        var doc = iframe.contentWindow.document;
        doc.open();
        doc.write('<html><head><title></title></head><body><img src="' + src + '"></body></html>');
        doc.close();
        return deferred.promise;
    }

Powiązane pytania