Làm mới hình ảnh với một cái mới ở cùng một url


333

Tôi đang truy cập vào một liên kết trên trang web của tôi sẽ cung cấp một hình ảnh mới mỗi khi nó được truy cập.

Vấn đề tôi gặp phải là nếu tôi cố tải hình ảnh ở chế độ nền rồi cập nhật hình ảnh trên trang, hình ảnh sẽ không thay đổi - mặc dù nó được cập nhật khi tôi tải lại trang.

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

Tiêu đề như FireFox thấy chúng:

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

Tôi cần phải làm mới chỉ hình ảnh đó trên trang. Có ý kiến ​​gì không?


Câu trả lời:


350

Hãy thử thêm một bộ đệm ẩn ở cuối url:

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

Điều này sẽ tự động thêm dấu thời gian hiện tại khi bạn đang tạo hình ảnh và nó sẽ làm cho trình duyệt tìm lại hình ảnh thay vì truy xuất hình ảnh trong bộ đệm.


26
Không phải là một giải pháp rất tốt vì nó sẽ lưu trữ bộ đệm (cả cục bộ và ngược dòng). Câu trả lời của Aya có cách giải quyết tốt hơn.
Tgr

1
Ngoài ra, hiển thị lại cùng một hình ảnh ở nơi khác, không có "bộ ngắt bộ đệm" sau, vẫn hiển thị phiên bản bộ nhớ cache cũ (ít nhất là trong firefox) cả? và # :(
T4NK3R

3
Bạn có thể tạo mã thậm chí ít hơn với điều này:'image.jpg?' + (+new Date())
Lev Lukomsky

4
Date.now()cái này
vp_arth

2
Tại sao khôngMath.random()
Gowtham Gopalakrishnan

227

Tôi đã thấy rất nhiều biến thể trong câu trả lời cho cách thực hiện điều này, vì vậy tôi nghĩ rằng tôi đã tóm tắt chúng ở đây (cộng thêm phương pháp thứ 4 của phát minh của riêng tôi):


(1) Thêm một tham số truy vấn bộ nhớ cache duy nhất vào URL, chẳng hạn như:

newImage.src = "image.jpg?t=" + new Date().getTime();

Ưu điểm: 100% đáng tin cậy, nhanh chóng & dễ hiểu và thực hiện.

Nhược điểm: Bỏ qua bộ nhớ đệm hoàn toàn, có nghĩa là sự chậm trễ không cần thiết và sử dụng băng thông bất cứ khi nào hình ảnh không thay đổi giữa các chế độ xem. Sẽ có khả năng lấp đầy bộ đệm của trình duyệt (và bất kỳ bộ đệm trung gian nào) với nhiều, nhiều bản sao của cùng một hình ảnh! Ngoài ra, yêu cầu sửa đổi URL hình ảnh.

Khi nào nên sử dụng: Sử dụng khi hình ảnh liên tục thay đổi, chẳng hạn như đối với nguồn cấp dữ liệu webcam trực tiếp. Nếu bạn sử dụng phương pháp này, hãy đảm bảo tự phục vụ hình ảnh bằng Cache-control: no-cachecác tiêu đề HTTP !!! (Thường thì điều này có thể được thiết lập bằng tệp .htaccess). Nếu không, bạn sẽ dần dần lấp đầy bộ nhớ cache với các phiên bản cũ của hình ảnh!


(2) Thêm tham số truy vấn vào URL chỉ thay đổi khi tệp thực hiện, ví dụ:

echo '<img src="image.jpg?m=' . filemtime('image.jpg') . '">';

(Đó là mã phía máy chủ PHP, nhưng điểm quan trọng ở đây chỉ là chuỗi truy vấn ? M = [file được sửa đổi lần cuối] được gắn vào tên tệp).

Ưu điểm: 100% đáng tin cậy, nhanh chóng & dễ hiểu và thực hiện, bảo tồn các lợi thế bộ nhớ đệm một cách hoàn hảo.

Nhược điểm: Yêu cầu sửa đổi URL hình ảnh. Ngoài ra, một chút công việc cho máy chủ - nó phải có quyền truy cập vào thời gian sửa đổi tập tin cuối cùng. Ngoài ra, yêu cầu thông tin phía máy chủ, do đó không phù hợp với giải pháp hoàn toàn chỉ dành cho khách hàng để kiểm tra hình ảnh được làm mới.

Khi nào nên sử dụng: Khi bạn muốn lưu trữ hình ảnh, nhưng có thể cần cập nhật chúng ở cuối máy chủ mà không thay đổi tên tệp. VÀ khi bạn có thể dễ dàng đảm bảo rằng chuỗi truy vấn chính xác được thêm vào mọi phiên bản hình ảnh trong HTML của bạn.


(3) Phục vụ hình ảnh của bạn với tiêu đề Cache-control: max-age=0, must-revalidatevà thêm một mã định danh phân đoạn duy nhất của memcache vào URL, chẳng hạn như:

newImage.src = "image.jpg#" + new Date().getTime();

Ý tưởng ở đây là tiêu đề kiểm soát bộ đệm đặt hình ảnh vào bộ đệm của trình duyệt, nhưng ngay lập tức đánh dấu chúng cũ, vì vậy và mỗi khi chúng được hiển thị lại, trình duyệt phải kiểm tra với máy chủ để xem chúng có thay đổi không. Điều này đảm bảo rằng bộ đệm HTTP của trình duyệt luôn trả về bản sao mới nhất của hình ảnh. Tuy nhiên, các trình duyệt thường sẽ sử dụng lại một bản sao trong bộ nhớ của hình ảnh nếu chúng có và thậm chí không kiểm tra bộ đệm HTTP của chúng trong trường hợp đó. Để ngăn chặn điều này, một định danh phân đoạn được sử dụng: So sánh các hình ảnh trong bộ nhớ srcbao gồm định danh phân đoạn, nhưng nó bị tước bỏ trước khi truy vấn bộ đệm HTTP. (Vì vậy, ví dụ, image.jpg#Aimage.jpg#Bcả hai có thể được hiển thị từ image.jpgmục trong bộ đệm HTTP của trình duyệt, nhưngimage.jpg#Bsẽ không bao giờ được hiển thị bằng dữ liệu hình ảnh được giữ lại trong bộ nhớ từ khi image.jpg#Ađược hiển thị lần cuối).

Ưu điểm: Sử dụng đúng các cơ chế bộ đệm HTTP và sử dụng các hình ảnh được lưu trong bộ nhớ cache nếu chúng không thay đổi. Hoạt động cho các máy chủ bị sặc trên chuỗi truy vấn được thêm vào URL hình ảnh tĩnh (vì các máy chủ không bao giờ thấy số nhận dạng phân đoạn - chúng chỉ dành cho sử dụng riêng của trình duyệt).

Nhược điểm: Dựa vào hành vi hơi mơ hồ (hoặc ít nhất là tài liệu kém) của các trình duyệt, liên quan đến hình ảnh có số nhận dạng phân đoạn trong URL của chúng (Tuy nhiên, tôi đã thử nghiệm thành công điều này trong FF27, Chrome33 và IE11). Vẫn không gửi yêu cầu xác nhận lại cho máy chủ cho mọi chế độ xem hình ảnh, điều này có thể quá mức nếu hình ảnh chỉ thay đổi hiếm khi và / hoặc độ trễ là một vấn đề lớn (vì bạn cần chờ phản hồi xác nhận lại ngay cả khi hình ảnh được lưu trong bộ nhớ cache vẫn còn tốt) . Yêu cầu sửa đổi URL hình ảnh.

Khi nào nên sử dụng: Sử dụng khi hình ảnh có thể thay đổi thường xuyên hoặc cần được làm mới liên tục bởi máy khách mà không có sự tham gia của tập lệnh phía máy chủ, nhưng bạn vẫn muốn có lợi thế của bộ nhớ đệm. Ví dụ: bỏ phiếu webcam trực tiếp cập nhật hình ảnh bất thường cứ sau vài phút. Hoặc, sử dụng thay vì (1) hoặc (2) nếu máy chủ của bạn không cho phép truy vấn trên các URL hình ảnh tĩnh.


(4) Buộc làm mới một hình ảnh cụ thể bằng Javascript, bằng cách trước tiên tải nó vào một ẩn <iframe>và sau đó gọi location.reload(true)vào iframe contentWindow.

Các bước là:

  • Tải hình ảnh để được làm mới vào iframe ẩn. Đây chỉ là một bước thiết lập - nó có thể được thực hiện trước khi làm mới thực tế, nếu muốn. Nó thậm chí không quan trọng nếu hình ảnh không tải ở giai đoạn này!

  • Khi đã xong, hãy bỏ trống tất cả các bản sao của hình ảnh đó trên (các) trang của bạn hoặc bất cứ nơi nào trong bất kỳ nút DOM nào (ngay cả các trang ngoài trang được lưu trữ trong các biến javascript). Điều này là cần thiết bởi vì trình duyệt có thể hiển thị hình ảnh từ một bản sao trong bộ nhớ cũ (đặc biệt là IE11): Bạn cần đảm bảo tất cả các bản sao trong bộ nhớ đều bị xóa, trước khi làm mới bộ đệm HTTP. Nếu mã javascript khác đang chạy không đồng bộ, bạn cũng có thể cần ngăn mã đó tạo các bản sao mới của hình ảnh được làm mới trong thời gian đó.

  • Gọi iframe.contentWindow.location.reload(true). Các truelực lượng bỏ qua bộ đệm, tải lại trực tiếp từ máy chủ và ghi đè lên bản sao được lưu trong bộ nhớ cache hiện có.

  • Một khi nó đã hoàn thành tái -loading, khôi phục lại hình ảnh blanked. Bây giờ họ sẽ hiển thị phiên bản mới từ máy chủ!

Đối với hình ảnh cùng tên miền, bạn có thể tải hình ảnh trực tiếp vào iframe. Đối với hình ảnh tên miền chéo, thay vào đó, bạn phải tải trang HTML từ tên miền có chứa hình ảnh trong <img>thẻ, nếu không, bạn sẽ gặp lỗi "Truy cập bị từ chối" khi cố gắng gọi iframe.contentWindow.reload(...).

Ưu điểm: Hoạt động giống như hàm image.reload () mà bạn muốn DOM có! Cho phép hình ảnh bằng cách lưu trữ thông thường (ngay cả với ngày hết hạn trong tương lai nếu bạn muốn, do đó tránh được xác nhận lại thường xuyên). Cho phép bạn làm mới một hình ảnh cụ thể mà không thay đổi URL cho hình ảnh đó trên trang hiện tại hoặc trên bất kỳ trang nào khác, chỉ sử dụng mã phía máy khách.

Nhược điểm: Dựa vào Javascript. Không được đảm bảo 100% để hoạt động chính xác trong mọi trình duyệt (mặc dù tôi đã thử nghiệm thành công điều này trong FF27, Chrome33 và IE11). Rất phức tạp so với các phương pháp khác.

Khi nào nên sử dụng: Khi bạn có một bộ sưu tập các hình ảnh tĩnh cơ bản mà bạn muốn lưu vào bộ nhớ cache, nhưng bạn vẫn cần có thể cập nhật chúng đôi khi và nhận được phản hồi trực quan ngay lập tức rằng bản cập nhật đã diễn ra. (Đặc biệt là khi chỉ làm mới toàn bộ trang trình duyệt sẽ không hoạt động, như trong một số ứng dụng web được xây dựng trên AJAX chẳng hạn). Và khi các phương thức (1) - (3) không khả thi vì (vì bất kỳ lý do gì), bạn không thể thay đổi tất cả các URL có khả năng hiển thị hình ảnh bạn cần cập nhật. (Lưu ý rằng sử dụng 3 phương thức đó, hình ảnh sẽ được làm mới, nhưng nếu một trang khác sau đó cố gắng hiển thị hình ảnh đó mà không có chuỗi truy vấn hoặc mã định danh phân đoạn thích hợp, thay vào đó, nó có thể hiển thị phiên bản cũ hơn).

Các chi tiết thực hiện điều này theo cách cổ tích mạnh mẽ và linh hoạt được đưa ra dưới đây:

Giả sử trang web của bạn chứa một pixel 1x1 pixel trống ở đường dẫn URL /img/1x1blank.gifvà cũng có tập lệnh PHP một dòng sau (chỉ bắt buộc để áp dụng làm mới bắt buộc cho hình ảnh tên miền chéo và có thể được viết lại bằng bất kỳ ngôn ngữ kịch bản phía máy chủ nào , tất nhiên) tại đường dẫn URL /echoimg.php:

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

Sau đó, đây là một triển khai thực tế về cách bạn có thể làm tất cả điều này trong Javascript. Trông có vẻ hơi phức tạp, nhưng có rất nhiều ý kiến ​​và chức năng quan trọng chỉ là forceImgReload () - hai hình ảnh đầu tiên chỉ trống và không trống, và nên được thiết kế để hoạt động hiệu quả với HTML của riêng bạn, vì vậy hãy mã hóa chúng thành làm việc tốt nhất cho bạn; nhiều sự phức tạp trong chúng có thể không cần thiết cho trang web của bạn:

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

Sau đó, để buộc làm mới hình ảnh nằm trên cùng một tên miền với trang của bạn, bạn chỉ có thể thực hiện:

forceImgReload("myimage.jpg");

Để làm mới hình ảnh từ một nơi khác (tên miền chéo):

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

Một ứng dụng nâng cao hơn có thể là tải lại hình ảnh sau khi tải phiên bản mới lên máy chủ của bạn, chuẩn bị giai đoạn ban đầu của quá trình tải lại đồng thời với tải lên, để giảm thiểu độ trễ tải lại có thể nhìn thấy cho người dùng. Nếu bạn đang thực hiện tải lên qua AJAX và máy chủ đang trả về một mảng JSON rất đơn giản [thành công, chiều rộng, chiều cao] thì mã của bạn có thể trông giống như thế này:

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

Lưu ý cuối cùng: Mặc dù chủ đề này là về hình ảnh, nhưng nó cũng có khả năng áp dụng cho các loại tệp hoặc tài nguyên khác. Ví dụ: ngăn chặn việc sử dụng tập tin cũ hoặc tập tin css hoặc thậm chí có thể làm mới các tài liệu PDF đã cập nhật (chỉ sử dụng (4) nếu được thiết lập để mở trong trình duyệt). Phương thức (4) có thể yêu cầu một số thay đổi đối với javascript ở trên, trong những trường hợp này.


Tôi thích ý tưởng của phương pháp 4, nhưng bạn không thể tải nội dung bên ngoài bằng Iframe, bạn có thể không? Tôi hiện đang sử dụng phương pháp 3 trong một ứng dụng web một trang, nhưng tôi không thích việc bạn phải tải lại toàn bộ trang để có được hình ảnh mới, ngay cả khi HTML của mẫu được tải lại.
Emilios1995

@Emilios: ... Ngoài ra, tôi không hiểu nhận xét của bạn về việc phải tải lại toàn bộ trang. Cả hai phương thức (3) và (4) đều có thể được triển khai trong javascript phía máy khách, mà không cần tải lại bất cứ thứ gì ngoại trừ một hình ảnh bạn đang làm mới. Đối với phương thức (3), điều đó chỉ có nghĩa là sử dụng javascript để thay đổi thuộc tính 'src' của hình ảnh của bạn từ (ví dụ) image.jpg#123thành image.jpg#124(hoặc bất cứ điều gì, miễn là bit sau khi thay đổi '#'). Bạn có thể làm rõ những gì bạn đang tải lại, và tại sao?
Doin

@Emilios: Bạn thực sự có thể tải nội dung bên ngoài (tên miền chéo) vào iframe ... nhưng sau đó bạn không thể truy cập contentWindowthông qua javascript để thực hiện reload(true)cuộc gọi đó là phần quan trọng của phương thức ... vì vậy, phương thức (4 ) sẽ không hoạt động cho nội dung tên miền chéo. Phát hiện tốt; Tôi sẽ cập nhật "Nhược điểm" để bao gồm điều đó.
Doin

@Emilios: Rất tiếc, không tôi sẽ không: Tôi nhận ra rằng một sửa chữa đơn giản (hiện đã có trong câu trả lời của tôi) cũng cho phép nó hoạt động cho các hình ảnh tên miền chéo (miễn là bạn có thể đặt tập lệnh phía máy chủ trên máy chủ của mình).
Doin

@pseudosavant: Thật không may, tôi nhận thấy điều này chỉ ~ 17 tháng sau, nhưng tôi rất tiếc phải nói với bạn rằng các chỉnh sửa của bạn đối với mã của tôi đã bị hỏng nặng. (Công bằng mà nói, tôi không nghĩ mã gọi lại mà tôi có ban đầu là đúng). Bây giờ tôi đã viết lại một phần (4) rộng rãi, cả giải thích & mã. Mã trước của bạn không bao giờ làm mờ hình ảnh (vì vậy nó có thể thất bại theo những cách kỳ lạ, đặc biệt là trong IE và đặc biệt là nếu hình ảnh được hiển thị ở nhiều nơi), nhưng tệ hơn là nó cũng xóa iframe ngay sau khi bắt đầu tải lại đầy đủ, điều đó có thể có nghĩa là nó chỉ làm việc không liên tục hoặc không làm gì cả. Lấy làm tiếc!
Doin

185

Thay thế cho ...

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

...có vẻ như...

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

... là đủ để đánh lừa bộ đệm của trình duyệt mà không bỏ qua bất kỳ bộ đệm ngược dòng nào, giả sử bạn đã trả lại đúng Cache-Controltiêu đề. Mặc dù bạn có thể sử dụng ...

Cache-Control: no-cache, must-revalidate

... bạn mất lợi ích của các tiêu đề If-Modified-Sincehoặc If-None-Match, vì vậy một cái gì đó như ...

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

... nên ngăn trình duyệt tải xuống lại toàn bộ hình ảnh nếu nó thực sự không thay đổi. Đã thử nghiệm và hoạt động trên IE, Firefox và Chrome. Khó chịu là nó thất bại trên Safari trừ khi bạn sử dụng ...

Cache-Control: no-store

... mặc dù điều này vẫn có thể thích hợp hơn để lấp đầy bộ đệm ngược dòng với hàng trăm hình ảnh giống hệt nhau, đặc biệt là khi chúng đang chạy trên máy chủ của riêng bạn. ;-)

Cập nhật (2014-09-28): Ngày nay, dường như Cache-Control: no-storecũng cần thiết cho Chrome.


1
Tuyệt quá! Sau nhiều thời gian cố gắng tải các hình ảnh web của tôi đang bị chậm trễ, tôi đã giải quyết nó bằng cách áp dụng giải pháp của bạn (với '#', sử dụng '?' Không hiệu quả với tôi). Cảm ơn nhiều!!!
304602

18
HAI bộ nhớ cache liên quan ở đây: Có bộ đệm HTTP thông thường của trình duyệt và bộ đệm trong bộ nhớ hình ảnh mà nó đã hiển thị gần đây. Bộ nhớ cache trong bộ nhớ sau này được lập chỉ mục bởi srcthuộc tính đầy đủ , do đó, việc thêm một mã định danh phân đoạn duy nhất đảm bảo rằng hình ảnh không bị kéo ra khỏi bộ nhớ. Nhưng số nhận dạng phân đoạn không được gửi như một phần của yêu cầu HTTP, do đó, bộ đệm HTTP thông thường sẽ được sử dụng như bình thường. Đó là lý do tại sao kỹ thuật này hoạt động.
Doin

Có một số bộ đệm tiêu đề. Thật ra tôi không biết tiếng Anh lắm, bạn có thể vui lòng cho tôi biết tôi nên sử dụng cái nào không?! Tôi muốn một cái gì đó không lưu trữ chỉ bộ ảnh được thay đổi (như hình ảnh xác thực) và lưu trữ những thứ khác. vậy Cache-Control: max-age=0, must-revalidatecó tốt cho tôi không?
Shafizadeh

Nó không làm việc cho tôi. Điều khác biệt duy nhất trong trường hợp của tôi là tôi có một hành động điều khiển url để lấy img từ db. Tôi đã có các đối số khác cho hành động của bộ điều khiển vì vậy tôi đã thêm nó dưới dạng "...... & convert = true & t =" + ngày mới (). GetTime (); và "...... & convert = true #" + Ngày mới (). getTime ();. Có điều gì tôi đang làm sai?
shaffooo

1
Để tránh chi phí tạo đối tượng và / hoặc gọi phương thức, bạn có thể sử dụng số nguyên tăng dần làm bộ đệm cache:newImage.src = "http://localhost/image.jpg#" + i++;
laindir

7

Sau khi tạo hình ảnh mới, bạn có xóa hình ảnh cũ khỏi DOM và thay thế nó bằng hình ảnh mới không?

Bạn có thể lấy hình ảnh mới mỗi cuộc gọi updateImage, nhưng không thêm chúng vào trang.

Có một số cách để làm điều đó. Một cái gì đó như thế này sẽ làm việc.

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

Sau khi làm việc đó, nếu vẫn còn vấn đề thì có lẽ đó là vấn đề lưu trữ giống như các câu trả lời khác.


3

Một câu trả lời là hackishly thêm một số tham số get như đã được đề xuất.

Một câu trả lời tốt hơn là phát ra một vài tùy chọn bổ sung trong tiêu đề HTTP của bạn.

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

Bằng cách cung cấp một ngày trong quá khứ, nó sẽ không được trình duyệt lưu vào bộ nhớ cache. Cache-Controlđã được thêm vào HTTP / 1.1 và thẻ phải xác nhận lại chỉ ra rằng các proxy không bao giờ phục vụ hình ảnh cũ ngay cả trong các tình huống giảm nhẹ và Pragma: no-cachekhông thực sự cần thiết cho các trình duyệt / bộ nhớ hiện đại hiện tại nhưng có thể giúp đỡ với một số triển khai cũ bị hỏng.


3
Điều này nghe có vẻ như đã hoạt động nhưng nó vẫn hiển thị cùng một hình ảnh, ngay cả với các bản hack. Tôi sẽ thêm thông tin tiêu đề cho câu hỏi.
Queue Hammer

Tôi chỉ nhận thấy rằng bạn đang làm mới cùng một thẻ img. Trình duyệt có thể phát hiện khi bạn đi đặt src mà src chưa thay đổi và do đó, nó không bận tâm để làm mới. (Vì kiểm tra này đang xảy ra ở cấp DOM và không liên quan gì đến mục tiêu). Điều gì xảy ra nếu bạn thêm "?" + số - vào url của hình ảnh được lấy?
Edward KMett

3

Tôi có một yêu cầu: 1) không thể thêm bất kỳ ?var=xxvào hình ảnh 2) nó sẽ hoạt động giữa các miền

Tôi thực sự thích tùy chọn số 4 trong câu trả lời này với một nhưng:

  • nó có vấn đề khi làm việc với tên miền chéo đáng tin cậy (và nó yêu cầu chạm vào mã máy chủ).

Cách nhanh chóng và bẩn thỉu của tôi là:

  1. Tạo iframe ẩn
  2. Tải trang hiện tại vào nó (vâng toàn bộ trang)
  3. iframe.contentWindow.location.reload(true);
  4. Đặt lại nguồn hình ảnh cho chính nó

Nó đây rồi

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

Vâng, tôi biết, setTimeout ... Bạn phải thay đổi điều đó thành các sự kiện onload thích hợp.


3
<img src='someurl.com/someimage.ext' onload='imageRefresh(this, 1000);'>

Sau đó bên dưới trong một số 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>

Và vì vậy, những gì nó làm là, khi hình ảnh tải, lên lịch để nó được tải lại trong 1 giây. Tôi đang sử dụng điều này trên một trang với các camera an ninh gia đình thuộc các loại khác nhau.


2

Điều cuối cùng tôi làm là có bản đồ máy chủ bất kỳ yêu cầu nào cho một hình ảnh trong thư mục đó đến nguồn mà tôi đang cố cập nhật. Sau đó, tôi đã hẹn giờ gắn một số vào cuối tên để DOM sẽ xem nó như một hình ảnh mới và tải nó.

Ví dụ

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

sẽ yêu cầu cùng một mã tạo hình ảnh nhưng nó sẽ trông giống như các hình ảnh khác nhau đối với trình duyệt.

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

8
Điều này sẽ có cùng một vấn đề về lưu trữ nhiều bản sao của hình ảnh như giải pháp chuỗi truy vấn (Paolo và một số người khác) và yêu cầu thay đổi máy chủ.
TomG

2

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')" />


3
vui lòng giải thích cho OP cách thức và lý do tại sao điều này giúp thay vì chỉ dán mã
nghĩa vào

Tôi không nghĩ đó ../showImage.phpFri May 01 2015 17:34:18 GMT+0200 (Mitteleuropäische Sommerzeit)là một tên tệp hợp lệ ... Ít nhất đây là những gì nó cố tải ...
Byteroulette

đổi path='../showImage.php';thànhpath='../showImage.php?';
BOOMik

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

đặt src riêng của nó làm src của nó.


1

Hãy thử sử dụng một chuỗi truy vấn vô giá trị để biến nó thành một url duy nhất:

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

Đã thêm WQS vào mã và xác minh rằng yêu cầu đang được chấp nhận và trình duyệt thấy phản hồi đến từ địa chỉ + WQS mà không cần làm mới hình ảnh.
Queue Hammer

1

Chủ yếu dựa trên mã # 4 Doin của, dưới đây ví dụ đơn giản hoá mã một chút lớn sử dụng document.writethay vì src trong iframeđể hỗ trợ CORS. Cũng chỉ tập trung vào việc làm hỏng bộ đệm của trình duyệt, không tải lại mọi hình ảnh trên trang.

Dưới đây được viết typescriptvà sử dụng thư viện lời hứa angular $ q , chỉ cần fyi, nhưng phải đủ dễ dàng để chuyển sang javilla javascript. Phương thức có nghĩa là sống trong một lớp bản thảo.

Trả về một lời hứa sẽ được giải quyết khi iframe hoàn tất tải lại. Không được thử nghiệm nhiều, nhưng hoạt động tốt cho chúng tôi.

    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;
    }

Để bảo vệ chống lại các lỗi XSS, bạn nên sử dụng + encodeURI(src) +để thoát đúng cách srctrong iframe.
Tino

1

Đoạn mã sau rất hữu ích để làm mới hình ảnh khi nhấn nút.

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')" />

Bị hạ bệ. Vì nó chỉ là một bản sao được sửa đổi một chút về mã của @ Mahmoud, nhưng ngược lại, điều này ở đây KHÔNG làm mới hình ảnh
Tino

0

Tôi đã giải quyết vấn đề này bằng cách gửi dữ liệu trở lại thông qua một servlet.

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

Sau đó, từ trang bạn chỉ cần cung cấp cho nó servlet với một số thông số để lấy tệp hình ảnh chính xác.

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

0

Đây là giải pháp của tôi. Nó rất đơn giản. Lịch trình khung có thể được tốt hơn.

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

0

Không cần new Date().getTime()shenanigans. Bạn có thể lừa trình duyệt bằng cách có một hình ảnh giả vô hình và sử dụng jQuery .load (), sau đó tạo một hình ảnh mới mỗi lần:

<img src="" id="dummy", style="display:none;" />  <!-- dummy img -->
<div id="pic"></div>

<script type="text/javascript">
  var url = whatever;
  // You can repeat the following as often as you like with the same url
  $("#dummy").load(url);
  var image = new Image();
  image.src = url;
  $("#pic").html("").append(image);
</script>

0

Giải pháp đơn giản: thêm tiêu đề này vào phản hồi:

Cache-control: no-store

Tại sao công việc này được giải thích rõ ràng tại trang có thẩm quyền này: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cache-Control

Nó cũng giải thích tại sao no-cachekhông hoạt động.

Các câu trả lời khác không hoạt động vì:

Caching.deletelà về bộ đệm mới mà bạn có thể tạo cho công việc ngoại tuyến, xem: https://web.dev/cache-api-quick-guide/

Các đoạn sử dụng # trong URL không hoạt động vì # bảo trình duyệt không gửi yêu cầu đến máy chủ.

Một bộ đệm bộ đệm với một phần ngẫu nhiên được thêm vào url hoạt động, nhưng cũng sẽ lấp đầy bộ đệm của trình duyệt. Trong ứng dụng của mình, tôi muốn tải xuống hình ảnh 5 MB cứ sau vài giây từ một web cam. Sẽ chỉ mất một giờ hoặc ít hơn để đóng băng hoàn toàn máy tính của bạn. Tôi vẫn không biết tại sao bộ đệm của trình duyệt không bị giới hạn ở mức tối đa hợp lý, nhưng đây chắc chắn là một bất lợi.


0

Tôi đã cải thiện tập lệnh từ AlexMA để hiển thị webcam của mình trên trang web với định kỳ tải lên một hình ảnh mới có cùng tên. Tôi gặp vấn đề là đôi khi hình ảnh bị nhấp nháy do hình ảnh bị hỏng hoặc hình ảnh không được tải đầy đủ. Để tránh nhấp nháy, tôi kiểm tra chiều cao tự nhiên của hình ảnh vì kích thước hình ảnh webcam của tôi không thay đổi. Chỉ khi chiều cao hình ảnh được tải phù hợp với chiều cao hình ảnh gốc, hình ảnh đầy đủ sẽ được hiển thị trên trang.

  <h3>Webcam</h3>
  <p align="center">
    <img id="webcam" title="Webcam" onload="updateImage();" src="https://www.your-domain.com/webcam/current.jpg" alt="webcam image" width="900" border="0" />

    <script type="text/javascript" language="JavaScript">

    // off-screen image to preload next image
    var newImage = new Image();
    newImage.src = "https://www.your-domain.com/webcam/current.jpg";

    // remember the image height to prevent showing broken images
    var height = newImage.naturalHeight;

    function updateImage()
    {
        // for sure if the first image was a broken image
        if(newImage.naturalHeight > height)
        {
          height = newImage.naturalHeight;
        }

        // off-screen image loaded and the image was not broken
        if(newImage.complete && newImage.naturalHeight == height) 
        {
          // show the preloaded image on page
          document.getElementById("webcam").src = newImage.src;
        }

        // preload next image with cachebreaker
        newImage.src = "https://www.your-domain.com/webcam/current.jpg?time=" + new Date().getTime();

        // refresh image (set the refresh interval to half of webcam refresh, 
        // in my case the webcam refreshes every 5 seconds)
        setTimeout(updateImage, 2500);
    }

    </script>
</p>

-3

Tôi đã sử dụng khái niệm dưới đây về ràng buộc hình ảnh đầu tiên với một url (bộ đệm) sai và tiếp theo liên kết nó với url hợp lệ.

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

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

Bằng cách này, tôi buộc trình duyệt phải làm mới bằng url hợp lệ.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.