簡體   English   中英

在retina.js庫中抑制404s

[英]Suppressing 404s in retina.js library

我們使用js lib retina.js ,它將低質量圖像與“視網膜”圖像交換( 大小乘以2)。 問題是,retina.js為每個無法找到的“視網膜”圖像拋出404。

我們擁有一個網站,用戶可以上傳他們自己的圖片,這些圖片很可能不是視網膜分辨率。

有沒有辦法阻止js投擲404s?

如果你不知道lib。 這是拋出404的代碼:

http = new XMLHttpRequest;
http.open('HEAD', this.at_2x_path);
http.onreadystatechange = function() {
    if (http.readyState != 4) {
        return callback(false);
    }

    if (http.status >= 200 && http.status <= 399) {
        if (config.check_mime_type) {
            var type = http.getResponseHeader('Content-Type');
            if (type == null || !type.match(/^image/i)) {
                return callback(false);
            }
        }

        RetinaImagePath.confirmed_paths.push(that.at_2x_path);
        return callback(true);
    } else {
        return callback(false);
    }
}
http.send();

我看到了一些選項來減輕這種情況。

增強並保持retina.js的HTTP調用結果緩存

對於設置為換出“1x”版本的任何給定“2x”圖像,retina.js首先通過XMLHttpRequest請求驗證圖像的可用性。 成功響應的路徑緩存在數組中,並下載映像。

以下更改可能會提高效率:

  • 可以緩存失敗的XMLHttpRequest驗證嘗試:目前,只有先前成功的'2x'路徑驗證嘗試才會被跳過。 因此,失敗的嘗試可能會再次發生。 在實踐中,這與初始加載頁面時驗證過程發生的情況無關。 但是,如果結果持續存在,跟蹤故障將防止重復出現404錯誤。

  • localStorage持續'2x'路徑驗證結果:在初始化期間,retina.js可以檢查localStorage以獲得結果緩存。 如果找到一個,則可以繞過已經遇到的“2x”圖像的驗證過程,並且可以下載或跳過“2x”圖像。 可以驗證新遇到的“2x”圖像路徑,並將結果添加到緩存中。 從理論上講,雖然localStorage可用,但每個瀏覽器的圖像只會出現一次404。 這將適用於域上任何頁面的頁面。

這是一個快速的檢查。 可能需要添加到期功能。

https://gist.github.com/4343101/revisions

使用HTTP重定向標頭

我必須指出,我對“服務器端”問題的把握充其量只是參差不齊 請帶上這個FWIW

另一個選項是服務器使用重定向代碼響應具有@2x字符且不存在的圖像請求。 看到這個相關的答案

特別是:

如果您重定向圖像並且它們是可緩存的,那么理想情況下,您可以在遙遠的未來為日期設置HTTP Expires標頭(以及相應的Cache-Control標頭),因此至少在后續訪問頁面時用戶不會再次進行重定向。

使用重定向響應將消除404並導致瀏覽器跳過后續嘗試訪問不存在的“2x”圖像路徑。

retina.js可以更具選擇性

可以修改視網膜以排除一些圖像。

與此相關的拉取請求: https//github.com/imulus/retinajs/commit/e7930be

根據pull請求,不是按標簽名稱查找<img>元素,而是可以使用CSS選擇器,這可以是retina.js的可配置選項之一。 可以創建一個CSS選擇器,用於過濾掉用戶上傳的圖像(以及預期不存在'2x'變體的其他圖像)。

另一種可能性是為可配置選項添加過濾功能。 可以在每個匹配的<img>元素上調用該函數; return true將導致下載“2x”變體,其他任何內容都會導致跳過<img>

基本的默認配置會從當前版本更改為:

var config = {
  check_mime_type: true,
  retinaImgTagSelector: 'img',
  retinaImgFilterFunc: undefined
};

Retina.init()函數將從當前版本更改為:

Retina.init = function(context) {
  if (context == null) context = root;

  var existing_onload = context.onload || new Function;

  context.onload = function() {
    // uses new query selector
    var images = document.querySelectorAll(config.retinaImgTagSelector), 
        retinaImages = [], i, image, filter;

    // if there is a filter, check each image
    if (typeof config.retinaImgFilterFunc === 'function') {
      filter = config.retinaImgFilterFunc;
      for (i = 0; i < images.length; i++) {
        image = images[i];
        if (filter(image)) {
          retinaImages.push(new RetinaImage(image));
        }
      }
    } else {
      for (i = 0; i < images.length; i++) {
        image = images[i];
        retinaImages.push(new RetinaImage(image));
      }
    }
    existing_onload();
  }
};

要在window.onload觸發之前付諸實踐,請致電:

window.Retina.configure({

  // use a class 'no-retina' to prevent retinajs
  // from checking for a retina version
  retinaImgTagSelector : 'img:not(.no-retina)',

  // or, assuming there is a data-owner attribute
  // which indicates the user that uploaded the image:
  // retinaImgTagSelector : 'img:not([data-owner])',

  // or set a filter function that will exclude images that have
  // the current user's id in their path, (assuming there is a
  // variable userId in the global scope)
  retinaImgFilterFunc: function(img) {
    return img.src.indexOf(window.userId) < 0;
  }
});

更新:清理並重組。 添加了localStorage增強功能。

簡短回答:僅使用客戶端JavaScript是不可能的

在瀏覽代碼和一些研究之后,在我看來,retina.js並沒有真正拋出404錯誤。

retina.js實際上正在做的是請求一個文件,只是根據錯誤代碼檢查它是否存在。 這實際上意味着它要求瀏覽器檢查文件是否存在。 瀏覽器是給你404的東西,沒有跨瀏覽器的方式來阻止它(我說“跨瀏覽器”,因為我只檢查了webkit)。

但是,如果這確實是一個問題,你可以做的是在服務器端做一些事情來完全防止404s。

基本上這將是,例如,/retina.php? image = YOUR_URLENCODED_IMAGE_PATH ,當視網膜圖像存在時,可以返回此請求...

{"isRetina": true, "path": "YOUR_RETINA_IMAGE_PATH"}}

如果它不...

{"isRetina": false, "path": "YOUR_REGULAR_IMAGE_PATH"}}

然后,您可以使用一些JavaScript調用此腳本並根據需要解析響應。 我並不是說這是唯一或最好的解決方案,只是一個可以工作的解決方案。

Retina JS支持圖像標簽上的屬性data-no-retina。 這樣它就不會試圖找到視網膜圖像。

尋找簡單解決方案的其他人很有幫助。

<img src="/path/to/image" data-no-retina />

我更喜歡控制哪些圖像被替換。

對於我創建了@ 2x for的所有圖像,我將原始圖像名稱更改為包含@ 1x。 (*見下面的注釋。)我稍微更改了retina.js,因此它只查看[name] @ 1x。[ext]圖像。

我在retina-1.1.0.js中替換了以下行:

retinaImages.push(new RetinaImage(image));

有以下幾行:

 if(image.src.match(/@1x\.\w{3}$/)) {
    image.src = image.src.replace(/@1x(\.\w{3})$/,"$1");
    retinaImages.push(new RetinaImage(image));
}

這使得retina.js只用@ 2x命名圖像替換@ 1x命名圖像。

(*注意:在探索這個時,似乎Safari和Chrome會自動用@ 2x圖像替換@ 1x圖像,即使沒有安裝retina.js。我也懶得跟蹤它,但我想它是一個功能最新的webkit瀏覽器。實際上,retina.js及其上述更改對於跨瀏覽器支持是必要的。)

解決方案之一是使用PHP:

從第一篇文章替換代碼:

        http = new XMLHttpRequest;
        http.open('HEAD', "/image.php?p="+this.at_2x_path);
        http.onreadystatechange = function() {
            if (http.readyState != 4) {
                return callback(false);
            }

            if (http.status >= 200 && http.status <= 399) {
                if (config.check_mime_type) {
                    var type = http.getResponseHeader('Content-Type');
                    if (type == null || !type.match(/^image/i)) {
                        return callback(false);
                    }
                }

                RetinaImagePath.confirmed_paths.push(that.at_2x_path);
                return callback(true);
            } else {
                return callback(false);
            }
        }
        http.send();

並在您的站點根目錄中添加名為“image.php”的文件:

<?php
 if(file_exists($_GET['p'])){
  $ext = explode('.', $_GET['p']);
  $ext = end($ext);
  if($ext=="jpg") $ext="jpeg";
  header("Content-Type: image/".$ext);
  echo file_get_contents($_GET['p']);
 }
?>

retina.js是靜態網頁上固定圖像的一個很好的工具,但是如果你要檢索用戶上傳的圖像,那么正確的工具就是服務器端。 我在這里想象PHP,但是同樣的邏輯可以應用於任何服務器端語言。

如果上傳圖像的良好安全習慣是不允許用戶通過直接URL訪問它們:如果用戶成功將惡意腳本上傳到您的服務器,他應該無法通過URL啟動它( www.yoursite.com/uploaded/mymaliciousscript.php )。 所以通過一些腳本<img src="get_image.php?id=123456" />檢索上傳的圖像通常是一個好習慣,如果你可以......(甚至更好,將上傳文件夾保留在文檔根目錄之外)

現在get_image.php腳本可以根據某些條件獲得適當的圖像123456.jpg或123456@2x.jpg。

http://retina-images.complexcompulsions.com/#setupserver的方法似乎非常適合您的情況。

首先,通過JS或CSS加載文件,在標題中設置一個cookie:

HEAD里面:

<script>(function(w){var dpr=((w.devicePixelRatio===undefined)?1:w.devicePixelRatio);if(!!w.navigator.standalone){var r=new XMLHttpRequest();r.open('GET','/retinaimages.php?devicePixelRatio='+dpr,false);r.send()}else{document.cookie='devicePixelRatio='+dpr+'; path=/'}})(window)</script>

在BODY開頭:

<noscript><style id="devicePixelRatio" media="only screen and (-moz-min-device-pixel-ratio: 2), only screen and (-o-min-device-pixel-ratio: 2/1), only screen and (-webkit-min-device-pixel-ratio: 2), only screen and (min-device-pixel-ratio: 2)">#devicePixelRatio{background-image:url("/retinaimages.php?devicePixelRatio=2")}</style></noscript>

現在每次調用上傳圖像的腳本時,它都會有一個cookie集,要求提供視網膜圖像(或不是)。

當然,您可以使用提供的retinaimages.php腳本輸出圖像,但您也可以根據您從數據庫生成和檢索圖像或從用戶隱藏上載目錄的方式對其進行修改以滿足您的需求。

因此,它不僅可以加載適當的圖像,而且如果安裝了GD2,並且您將原始上載的圖像保留在服務器上,它甚至可以調整其大小並相應地裁剪並將2個緩存的圖像大小保存在服務器上。 在retinaimages.php源代碼中,您可以看到(並復制)它的工作原理:

<?php
    $source_file = ...
    $retina_file = ....

    if (isset($_COOKIE['devicePixelRatio'])) {
        $cookie_value = intval($_COOKIE['devicePixelRatio']);
    }
    if ($cookie_value !== false && $cookie_value > 1) {
        // Check if retina image exists
        if (file_exists($retina_file)) {
            $source_file = $retina_file;
        }
    }
    ....



    header('Content-Length: '.filesize($source_file), true);
    readfile($source_file); // or read from db, or create right size.. etc..

?>

優點:圖像只加載一次(3G上的視網膜用戶至少不會加載1x + 2x圖像),如果啟用了cookie,即使沒有JS ,也可以輕松打開和關閉,無需使用蘋果命名約定。 您加載圖像12345,您獲得了正確的設備DPI。

使用url重寫,您甚至可以通過將/get_image/1234.jpg重定向到/get_image.php?id=1234.jpg來使其完全透明。

我的建議是你認識到404錯誤是真正的錯誤,並按照你應該的方式修復它們,即提供Retina圖形。 您使您的腳本與Retina兼容,但您沒有通過使您的圖形工作流與Retina兼容來完成該循環。 因此,Retina圖形實際上是丟失的。 無論圖形工作流程開始時是什么,工作流程的輸出必須是2個圖像文件,低分辨率和Retina 2x。

如果用戶上傳的照片為3000x2400,您應該將其視為照片的Retina版本,將其標記為2x,然后使用服務器端腳本生成較小的1500x1200非Retina版本,而不使用2x。 這兩個文件一起構成一個1500x1200 Retina兼容圖像,無論顯示器是否為Retina,都可以在1500x1200的Web上下文中顯示。 您不必關心,因為您有與Retina兼容的圖像和Retina兼容的網站。 RetinaJS腳本是唯一一個必須關心客戶端是否使用Retina的腳本。 因此,如果您要從用戶收集照片,除非您生成2個低分辨率和高分辨率的文件,否則您的任務將無法完成。

典型的智能手機拍攝的照片尺寸是智能手機顯示屏的10倍以上。 所以你應該總是有足夠的像素。 但是如果你得到的是非常小的圖像,比如500px,那么你可以在你的服務器端圖像縮小腳本中設置一個斷點,以便在下面,上傳的照片用於低分辨率版本,腳本制作2x拷貝這將不會比非Retina圖像好,但它將與Retina兼容。

有了這個解決方案,你的整個問題“是2x圖像在哪里?”消失了,因為它總是在那里。 與Retina兼容的網站將很樂意使用您的Retina兼容照片數據庫,無任何投訴。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM