Подтвердить что ты не робот

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

Мы используем 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();
4b9b3361

Ответ 1

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

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

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

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

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

  • Результат проверки состояния Persist '2x' в localStorage: во время инициализации 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 можно сделать более селективным

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

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

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

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

Ответ 2

Короткий ответ: его невозможно использовать только на клиентской стороне JavaScript

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

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

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

По существу это будет, например, /retina.php?image= YOUR_URLENCODED_IMAGE_PATH запрос, который может вернуть это, когда изображение сетчатки существует...

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

и если это не...

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

Тогда вы можете использовать JavaScript для вызова script и проанализировать ответ по мере необходимости. Я не утверждаю, что это единственное или лучшее решение, которое будет работать.

Ответ 3

Retina JS поддерживает атрибут data-no-retina на теге изображения. Таким образом, он не будет пытаться найти изображение сетчатки.

Полезно для других людей, которые ищут простое решение.

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

Ответ 4

Я предпочитаю немного больше контроля над заменой изображений.

Для всех образов, которые я создал @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.

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

Ответ 5

Одним из решений является использование 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']);
 }
?>

Ответ 6

retina.js - отличный инструмент для фиксированных изображений на статических веб-страницах, но если вы загружаете загруженные пользователем изображения, правильным инструментом является серверная сторона. Я представляю PHP здесь, но та же логика может применяться к любому серверному языку.

При условии, что хорошая привычка к безопасности для загруженных изображений заключается в том, чтобы не допустить, чтобы пользователи достигли их прямым URL-адресом: если пользователю удастся загрузить вредоносный script на ваш сервер, он не сможет запустить его с помощью URL-адреса (www.yoursite.com/uploaded/mymaliciousscript.php). Таким образом, обычно хорошая привычка извлекать загруженные изображения через некоторые script <img src="get_image.php?id=123456" />, если вы можете... (и даже лучше, сохранить папку загрузки из корня документа)

Теперь get_image.php script может получить соответствующее изображение 123456.jpg или [email protected] в зависимости от некоторых условий.

Подход http://retina-images.complexcompulsions.com/#setupserver кажется идеальным для вашей ситуации.

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

Внутри 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>

В начале ТЕЛА:

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

Теперь каждый раз, когда вы вызываете script для загрузки загруженных изображений, у него будет набор файлов cookie с запросом на изображения сетчатки (или нет).

Конечно, вы можете использовать предоставленный retinaimages.php script для вывода изображений, но вы также можете изменить его для удовлетворения ваших потребностей в зависимости от того, как вы производите и извлекаете изображения из базы данных или скрываете каталог загрузки от пользователей.

Таким образом, не только он может загрузить соответствующее изображение, но если 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 изображения), работает даже без JS, если файлы cookie включены, могут быть легко включены и выключены, нет необходимости использовать соглашения об именах Apple. Вы загружаете изображение 12345 и получаете правильный DPI для своего устройства.

С переписыванием URL вы можете даже сделать его полностью прозрачным, перенаправив /get _image/1234.jpg в/get_image.php?id=1234.jpg

Ответ 7

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

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

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

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