Подавление 404s в сетчатке.библиотека js

мы используем JS lib сетчатки.js который обменивает изображения низкого качества с изображениями" сетчатки " (размер раз 2). Проблема в сетчатке.js бросает 404 для каждого изображения" сетчатки", которое не может быть найдено.

у нас есть сайт, где пользователи могут загружать свои собственные фотографии, которые, скорее всего, не в Retina-разрешении.

нет ли способа предотвратить 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();

7 ответов


есть несколько вариантов, которые я вижу, чтобы избежать этого.

повышение и сохранение сетчатки.кэширование результатов HTTP-вызовов js

для любого заданного изображения "2x", которое установлено для замены версии "1x", retina.js сначала проверяет доступность изображения через XMLHttpRequest запрос. Пути с успешными ответами кэшируются в массиве, и изображение загружается.

следующие изменения могут повысить эффективность:

  • не удалось XMLHttpRequest попытки проверки могут быть кэшированы: в настоящее время попытка проверки пути " 2x " пропускается, только если она ранее была успешной. Поэтому неудачные попытки могут повториться. На практике это не имеет большого значения, поскольку процесс проверки происходит при первоначальной загрузке страницы. Но, если результаты сохраняются, отслеживание сбоев предотвратит повторение 404 ошибок.

  • результаты проверки пути Persist ' 2x ' в localStorage: во время инициализации, сетчатка.js может проверить localStorage для кэширования результатов. Если он найден, процесс проверки изображений "2x", которые уже были обнаружены, можно обойти, а изображение " 2x " можно загрузить или пропустить. Новые пути Изображения encounterd " 2x " могут быть проверены и результаты добавлены в кэш. Теоретически, пока localStorage доступно, 404 будет происходить только один раз для изображения на основе каждого браузера. Это будет применяться через страницы для любой страницы на домен.

вот быстрый осмотр. Вероятно, потребуется добавить функциональность Expiration.

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

используйте заголовок перенаправления HTTP

я должен отметить, что мое понимание" серверных " вопросов пятнистой, в лучшем случае. Пожалуйста, возьмите это FWIW

другой вариант ответа сервера с кодом переадресации запросов изображения the @2x символы и не существует. См.это связано ответа.

в частности:

если вы перенаправляете изображения, и они кэшируемы, вы бы идеально установили заголовок HTTP Expires (и соответствующий заголовок Cache-Control) для даты в далеком будущем, поэтому, по крайней мере, при последующих посещениях страницы пользователям не придется снова проходить перенаправление.

используя ответ перенаправления, вы избавитесь от 404s и заставьте браузер пропустить последующие попытки доступа к путям изображений "2x", которые не существуют.

сетчатки.js можно сделать более избирательным

retinajs можно изменить, чтобы исключить некоторые изображения из рассмотрения.

запрос на вытягивание, связанный с этим:https://github.com/imulus/retinajs/commit/e7930be

в запросе pull, вместо того, чтобы найти <img> элементы по имени тега, селектор CSS может быть использован, и это может быть один из сетчатка.настраиваемые параметры js. Можно создать селектор CSS, который будет отфильтровывать загруженные пользователем изображения (и другие изображения, для которых вариант "2x", как ожидается, не существует).

еще одна возможность-добавить функцию фильтра в настраиваемые параметры. Функция может быть вызвана на каждом matched <img> элемент; a 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 на стороне клиента

после просмотра кода и небольшого исследования мне кажется, что сетчатка.js не действительно бросая 404 ошибки.

что сетчатки.JS фактически делает запрос файла и просто выполняет проверку того, существует ли он на основе кода ошибки. Что на самом деле означает, что это запрос браузера, чтобы проверить, если файл существует. в браузер-это то, что дает вам 404, и нет кросс-браузера, чтобы предотвратить это (я говорю "кросс-браузер", потому что я только проверил webkit).

однако, что вы можете сделать, если это действительно проблема, это сделать что-то на стороне сервера, чтобы предотвратить 404s вообще.

по существу это будет ,например, / retina.РНР?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, я изменил исходное имя изображения, чтобы включить @1x. (*См. Примечание ниже. Я изменил сетчатку.в JS немного, так что он смотрит только на [имя]@1х.[доб] картинки.

Я заменил следующую строку в 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})$/,"");
    retinaImages.push(new RetinaImage(image));
}

это делает его таким, что сетчатка.js заменяет только @ 1X именованные изображения с @2х изображений по имени.

(*Примечание: при изучении этого кажется, что Safari и Chrome автоматически заменяют изображения @1x на изображения @2x, даже без retina.Яш установлены. Я слишком ленив, чтобы отслеживать это, но я бы предположил, что это функция с последними браузерами webkit. Как есть, сетчатка.js и вышеуказанные изменения к нему необходимы для поддержки кросс-браузера.)


одним из решений является использование PHP:

заменить код с 1-го поста на:

        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']);
 }
?>

сетчатки.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 кажется идеальным для вашей ситуации.

сначала вы устанавливаете cookie в своем заголовке загрузка файла через JS или CSS:

внутри:

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

в начале тела:

<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 с запросом изображений retina (или нет).

конечно, вы можете использовать предоставленные 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..

?>

плюсы: изображение загружается только один раз (пользователи retina на 3G, по крайней мере, не загружают изображения 1x+2x), работает даже без JS если cookies включены, могут быть легко включены и выключены, нет необходимости использовать Соглашения об именах apple. Вы загружаете изображение 12345 и получаете правильный DPI для вашего устройства.

при перезаписи url вы даже можете сделать его полностью прозрачным, перенаправив /get_image / 1234.jpg to / get_image.РНР?id=1234.формат JPG


мое предложение заключается в том, что вы признаете 404 ошибки истинными ошибками и исправляете их так, как вы должны, то есть предоставлять графику Retina. Вы сделали свои скрипты совместимыми с Retina, но вы не завершили круг, сделав свой графический рабочий процесс совместимым с Retina. Таким образом, графика Retina фактически отсутствует. Независимо от того, что происходит в начале графического рабочего процесса, выход рабочего процесса должен быть 2 файлами изображений, низким разрешением и сетчаткой 2 раза.

Если пользователь загружает фотографию 3000x2400, вы должны считать, что это версия Retina фотографии, отметьте ее 2x, а затем используйте серверный скрипт для создания меньшей версии 1500x1200 без Retina, без 2x. 2 файла вместе составляют одно сетчатка-совместимое изображение 1500x1200, которое может быть отображено в веб-контексте на 1500x1200 независимо от того, является ли дисплей сетчаткой или нет. Вам не нужно заботиться, потому что у вас есть сетчатка-совместимое изображение и сетчатка-совместимый вебсайт. Сценарий RetinaJS-единственный, который должен заботиться о том, использует ли клиент Retina или нет. Поэтому, если вы собираете фотографии от пользователей, ваша задача не будет завершена, если вы не создадите 2 файла, как с низким, так и с высоким разрешением.

типичный смартфон захватывает фотографию, которая больше, чем 10x размер дисплея смартфона. Поэтому у вас всегда должно быть достаточно пикселей. Но если вы получаете действительно маленькие изображения, такие как 500px, вы можете установить точку останова на стороне сервера сценарий уменьшения изображения, так что ниже, загруженная фотография используется для версии с низким разрешением, и скрипт делает копию 2x, которая будет не лучше, чем изображение без сетчатки, но она будет совместима с сетчаткой.

с этим решением вся ваша проблема "есть ли изображение 2x там или нет?- уходит, потому что он всегда здесь. Сетчатка-совместимый веб-сайт будет просто счастливо использовать сетчатку-совместимую базу данных фотографий без каких-либо жалоб.