web-dev-qa-db-ja.com

retina.jsライブラリで404を抑制

Js lib retina.js を使用します。これは、低品質の画像を「網膜」画像(サイズの2倍)と交換します。問題は、retina.jsが見つからないすべての「網膜」画像に対して404をスローすることです。

私たちは、ユーザーが自分の写真をアップロードできるサイトを所有していますが、これはおそらく網膜の解像度ではありません。

Jsが404をスローするのを防ぐ方法はありませんか?

あなたが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();
25
j7nn7k

これを軽減するために、私が見るいくつかのオプションがあります。

Retina.jsのHTTP呼び出し結果のキャッシュを強化して永続化する

「1x」バージョンをスワップアウトするように設定されている任意の「2x」イメージについて、retina.jsは最初にXMLHttpRequestリクエストを介してイメージの可用性を確認します。応答が成功したパスは配列にキャッシュされ、画像がダウンロードされます。

次の変更により、効率が向上する場合があります。

  • 失敗したXMLHttpRequest検証の試行はキャッシュできます:現在、「2x」パス検証の試行は、以前に成功した場合にのみスキップされます。したがって、失敗した試行は再発する可能性があります。実際には、ページが最初にロードされたときに検証プロセスが行われるため、これはそれほど重要ではありません。ただし、結果が持続する場合は、障害を追跡することで404エラーの再発を防ぐことができます。

  • 永続的な「2x」パス検証の結果はlocalStorage:初期化中に、retina.jsはlocalStorageで結果キャッシュをチェックできます。見つかった場合は、すでに検出された「2x」イメージの検証プロセスをバイパスして、「2x」イメージをダウンロードまたはスキップできます。新たに検出された「2x」イメージパスを検証し、結果をキャッシュに追加できます。理論的には、localStorageが利用可能ですが、404はブラウザごとに画像に対して1回だけ発生します。これは、ドメイン上のすべてのページのページ全体に適用されます。

これが簡単な後処理です。有効期限機能を追加する必要があるでしょう。

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

HTTPリダイレクトヘッダーを採用する

私の「サーバー側」の問題の把握は、せいぜいspottyであることに注意する必要があります。このFWIWをどうぞ

もう1つのオプションは、サーバーが_@2x_文字を含み、存在しない画像要求のリダイレクトコードで応答することです。 この関連する回答 を参照してください。

特に:

画像をリダイレクトしてキャッシュ可能にする場合は、理想的には、HTTP Expiresヘッダー(および適切なCache-Controlヘッダー)を遠い将来の日付に設定するので、少なくともその後のページへのアクセスでは、ユーザーはリダイレクトを再度実行します。

リダイレクト応答を使用すると、404が削除され、ブラウザは、存在しない「2x」イメージパスにアクセスする後続の試行をスキップします。

retina.jsをより選択的にすることができます

retinajsを変更して、一部の画像を考慮から除外することができます。

これに関連するプルリクエスト: https://github.com/imulus/retinajs/commit/e7930be

プルリクエストごとに、タグ名で_<img>_要素を見つける代わりに、CSSセレクターを使用できます。これは、retina.jsの構成可能なオプションの1つです。ユーザーがアップロードした画像(および「2x」バリアントが存在しないと予想されるその他の画像)を除外するCSSセレクターを作成できます。

もう1つの可能性は、構成可能なオプションにフィルター機能を追加することです。この関数は、一致した_<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拡張機能を追加しました。

28
tiffon

短い答え:クライアント側のJavaScriptのみを使用することはできません

コードを閲覧し、少し調べたところ、retina.jsは本当に 404エラーをスローしていないように見えます。

Retina.jsが実際に行っているのは、ファイルを要求し、エラーコードに基づいてファイルが存在するかどうかのチェックを実行することです。つまり、実際にはそうですファイルが存在するかどうかをブラウザに確認するように求めます。ブラウザは404を提供するものであり、それを防ぐクロスブラウザの方法はありません(私は「クロスブラウザ」と言うのは私だけなのでチェックされたウェブキット)。

ただし、これが本当に問題である場合にできることは、404を完全に防ぐためにサーバー側で何かを行うことです。

基本的に、これは、たとえば、/ retina.php?image = YOUR_URLENCODED_IMAGE_PATH網膜画像が存在する場合にこれを返すことができるリクエストになります。

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

そうでなければこれ...

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

次に、JavaScriptでこのスクリプトを呼び出し、必要に応じて応答を解析することができます。 私はそれが唯一または最良の解決策であるとは主張していません。うまくいくものだけです。

8
KTastrophy

Retina JSは、画像タグの属性data-no-retinaをサポートしています。このようにして、網膜画像を見つけようとはしません。

簡単な解決策を探している他の人に役立ちます。

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

どの画像を置き換えるかをもう少し制御したいです。

@ 2xを作成したすべての画像について、元の画像名を変更して@ 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は@ 1xの名前付き画像のみを@ 2xの名前付き画像に置き換えるようになります。

(*注:これを調べると、Retina.jsがインストールされていなくても、SafariとChromeは@ 1xイメージを@ 2xイメージに自動的に置き換えるようです。これを追跡するのは面倒です。しかし、これは最新のWebkitブラウザーの機能だと思います。現状では、クロスブラウザーのサポートにはretina.jsと上記の変更が必要です。)

3
MrTemple

解決策の1つは、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']);
 }
?>
2
Pawel Pawlowski

retina.jsは、静的Webページ上の固定画像に適したツールですが、ユーザーがアップロードした画像を取得する場合、適切なツールはサーバー側です。ここではPHP)を想像していますが、同じロジックをどのサーバーサイド言語にも適用できます。

アップロードされた画像の優れたセキュリティ習慣は、ユーザーが直接URLで画像にアクセスできないようにすることです。ユーザーが悪意のあるスクリプトをサーバーにアップロードすることに成功した場合、ユーザーはそれを介して起動できないようにする必要があります。 url(www.yoursite.com/uploaded/mymaliciousscript.php)。したがって、通常、アップロードされた画像をスクリプトで取得することをお勧めします<img src="get_image.php?id=123456" />可能であれば...(さらに良いことに、アップロードフォルダをドキュメントルートから除外します)

これで、get_image.phpスクリプトは、いくつかの条件に応じて、適切な画像123456.jpgまたは[email protected]を取得できます。

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

?>

長所:画像は1回だけ読み込まれ(3Gの網膜ユーザーは少なくとも1x + 2x画像を読み込まない)、さらに機能しますJSなし Cookieが有効になっている場合は、簡単にオンとオフを切り替えることができます。 Apple命名規則を使用してください。イメージ12345をロードすると、デバイスに適したDPIが得られます。

URLの書き換えでは、/ get_image/1234.jpgを/get_image.php?id=1234.jpgにリダイレクトすることで、完全に透過的にすることもできます。

1
FrancescoMM

私の提案は、404エラーを真のエラーとして認識し、Retinaグラフィックを提供するという想定どおりに修正することです。スクリプトをRetina互換にしましたが、グラフィックワークフローをRetina互換にしてサークルを完成させませんでした。したがって、Retinaグラフィックは実際には欠落しています。グラフィックワークフローの開始時に入力されるものが何であれ、ワークフローの出力は2つの画像ファイル(低解像度とRetina 2x)である必要があります。

ユーザーが3000x2400の写真をアップロードした場合は、その写真をRetinaバージョンと見なし、2xとマークしてから、サーバーサイドスクリプトを使用して、2xなしの1500x1200の非Retinaバージョンを生成する必要があります。 2つのファイルが一緒になって1つの1500x1200Retina互換画像を構成し、ディスプレイがRetinaであるかどうかに関係なく、1500x1200でWebコンテキストに表示できます。 Retina互換の画像とRetina互換のWebサイトがあるので、気にする必要はありません。 RetinaJSスクリプトは、クライアントがRetinaを使用しているかどうかを気にする必要がある唯一のスクリプトです。したがって、ユーザーから写真を収集している場合、低解像度と高解像度の両方の2つのファイルを生成しない限り、タスクは完了しません。

一般的なスマートフォンは、スマートフォンのディスプレイの10倍以上のサイズの写真をキャプチャします。したがって、常に十分なピクセルが必要です。ただし、500pxのような非常に小さい画像を取得している場合は、サーバー側の画像縮小スクリプトにブレークポイントを設定して、その下でアップロードされた写真が低解像度バージョンに使用され、スクリプトが2倍のコピーを作成するようにすることができます。これは、Retina以外の画像よりも優れているわけではありませんが、Retinaと互換性があります。

このソリューションでは、「2倍の画像が存在するかどうか」という問題全体が発生します。それは常にそこにあるので、消えます。 Retina互換のWebサイトでは、Retina互換の写真データベースを問題なく使用できます。

0
Simon White