25

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

私たちは、ユーザーが Retina 解像度ではない可能性が最も高い自分の写真をアップロードできるサイトを所有しています。

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

ライブラリがわからない場合。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();
4

7 に答える 7

28

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

retina.js の HTTP 呼び出し結果のキャッシュを強化して保持する

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

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

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

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

これが簡単なワークアップです。おそらく有効期限機能を追加する必要があります。

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

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

「サーバー側」の問題についての私の把握は、せいぜいむらがあることに注意する必要があります。このFWIWを受け取ってください

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

特に:

画像をリダイレクトし、それらがキャッシュ可能である場合は、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>要素ごとに呼び出すことができます。areturn 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強化を追加しました。

于 2012-12-15T11:06:17.767 に答える
8

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

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

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

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

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

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

そうでない場合、これは...

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

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

于 2012-12-14T17:26:57.830 に答える
3

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

@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 とそれに対する上記の変更は、クロスブラウザーのサポートに必要です)。

于 2013-11-25T20:57:24.643 に答える
2

解決策の 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']);
 }
?>
于 2012-12-19T10:07:44.167 に答える
1

retina.js は、静的な Web ページの画像を固定するための優れたツールですが、ユーザーがアップロードした画像を取得する場合、適切なツールはサーバー側です。ここでは 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.complexcompulls.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>

これで、アップロードされた画像を取得するスクリプトが呼び出されるたびに、Retina 画像を要求する (または要求しない) 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 の Retina ユーザーは、少なくとも 1x+2x の画像を読み込みません)、 Cookie が有効になっている場合はJS がなくても機能し、簡単にオンとオフを切り替えることができ、Apple の命名規則を使用する必要はありません。イメージ 12345 をロードすると、デバイスの正しい DPI が得られます。

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

于 2012-12-18T15:24:26.313 に答える