Программно проверять и обновлять только если изображение изменилось - PullRequest
2 голосов
/ 18 марта 2011

У меня есть приложение, которое время от времени обновляет изображение.Интервал обновления не предсказуем.Само изображение автоматически обновляется на веб-сервере через rename().Это все, что делает это приложение, и на стороне Apache не должно быть никаких изменений, так что веб-сервер может продолжать обслуживать только статические файлы.

Существует некоторый сценарий AJAX, который отображает содержимое и обновляет это изображение, когдаэто изменилось.Это делается с помощью опроса.В простой версии JavaScript использовался счетчик, и обновленный каждую секунду вытягивал изображение, добавляя отметку времени запроса.Однако в 99% случаев это тянет изображение без изменений.

Текущая не столь наивная версия использует XMLHttpRequest aka.AJAX для проверки заголовка If-Modified-Since-, и если обнаруживается изменение, вызывается обновление.

Вопрос теперь в том, есть ли лучший способ заархивировать этот эффект? Возможно, посмотрите последний абзац этого текста, прежде чем погрузиться в это;)

Вот основные фрагменты кода текущей версии.Обратите внимание, что код отредактирован для краткости, поэтому var инициализация оставлена ​​без внимания и удалены некоторые строки, которые не представляют интереса.

Сначала обычная, слегка расширенная привязка AJAX:

// partly stolen at http://snippets.dzone.com/posts/show/2025
function $(e){if(typeof e=='string')e=document.getElementById(e);return e};
ajax={};
ajax.collect=function(a,f){var n=[];for(var i=0;i<a.length;i++){var v=f(a[i]);if(v!=null)n.push(v)}return n};
ajax.x=function(){try{return new XMLHttpRequest()}catch(e){try{return new ActiveXObject('Msxml2.XMLHTTP')}catch(e){return new ActiveXObject('Microsoft.XMLHTTP')}}};
ajax.send=function(u,f,m,a,h){var x=ajax.x();x.open(m,u,true);x.onreadystatechange=function(){if(x.readyState==4)f(x.responseText,x,x.status==0?200:x.status,x.getResponseHeader("Last-Modified"))};if(m=='POST')x.setRequestHeader('Content-type','application/x-www-form-urlencoded');if(h)h(x);x.send(a)};
ajax.get=function(url,func){ajax.send(url,func,'GET')};
ajax.update=function(u,f,lm){ajax.send(u,f,'GET',null,lm?function(x){x.setRequestHeader("If-Modified-Since",lm)}:lm)};
ajax.head=function(u,f,lm){ajax.send(u,f,'HEAD',null,lm?function(x){x.setRequestHeader("If-Modified-Since",lm)}:lm)};

Основная часть HTML, она включает в себя 2 изображения, которые переворачиваются после загрузки, и третье (не упомянутое в фрагментах кода) для отображения заархивированных версий и т. Д., Что также предотвращает переключение обновлений:

</head><body onload="init()">
<div id="shower"><img id="show0"/><img id="show1"/><img id="show2"/></div>

Начальная часть включает в себя таймер.Это немного больше, чтобы компенсировать сетевые задержки на медленных каналах, снизить частоту опроса и т. Д.:

function init()
{
window.setInterval(timer,500);
for (var a=2; --a>=0; )
  {
    var o=$("show"+a);
    o.onload  = loadi;
  }
disp(0);
}

function disp(n)
{
shown=n;
window.setTimeout(disp2,10);
}

function disp2()
{
hide("show0");
hide("show1");
hide("show2");
show("show"+shown);
}

function hide(s)            
{                           
$(s).style.display="none";  
}                           
function show(s)            
{                           
$(s).style.display="inline";
}                           

function timer(e)
{
if (waiti && !--waiti)
  dorefresh();
nextrefresh();
}

function nextrefresh()
{
if (sleeps<0)
  sleeps = sleeper;
if (!--sleeps)
  pendi = true;
if (pendi && !waiti)
  dorefresh();
}

Время от времени вызывается dorefresh (), чтобы вытащить HEAD, отслеживая If-Изменено-С:

function dorefresh()
{
waiti = 100; // allow 50s for this request to take
ajax.head("test.jpg",checkrefresh,lm);
}

function checkrefresh(e,x,s,l)
{
if(!l)
  {
    // not modified
    lmc++;
    waiti = 0;
  }
else
  {
    lmc=0;
    lm=l;

    $("show"+loadn).src = "test.jpg?"+stamp();

    waiti=100;
  }
pendi=false;
sleeper++;
if (sleeper>maxsleep)
  sleeper = maxsleep;
sleeps=0;
nextrefresh();
}

function stamp()
{
return new Date().getTime();
}

Когда изображение загружено, оно переворачивается в поле зрения.shown обычно равно 0 или 1:

function loadi()
{
waiti=0;
$("show"+loadn).style.opacity=1;
if (shown<2)
  disp(loadn);
loadn=1-loadn;
}

Обратите внимание, что я тестировал этот код только в браузерах на основе Webkit.К сожалению, я не могу предоставить рабочий пример, так как мой источник обновлений не является общедоступным.Также прошу прощения за то, что код довольно быстро-грязный.

Строго говоря, достаточно только HEAD, мы могли бы взглянуть на заголовок Last-Modified, конечно.Но этот рецепт здесь также работает для GET запросов в ситуации, не связанной с изображением.

AJAX GET в сочетании с изображениями имеет меньший смысл, так как это использует изображение как двоичные данные.Конечно, я мог бы преобразовать это в некоторое встроенное изображение, но на больших изображениях (таких как мое) это будет превышать максимальную длину URL.

Одна вещь, которую возможно сделать, это использовать кеш браузера.То есть вытащите изображение, используя ajax.update, а затем повторно отобразите изображение из кэша.

Однако это зависит от стратегии кэширования браузера.На мобильных устройствах изображение может быть слишком большим для кэширования, в этом случае оно передается дважды.Это неправильно, поскольку обычно мобильные устройства имеют медленные и более важные дорогостоящие каналы передачи данных.

Мы могли бы использовать этот метод, если веб-сервер напишет текстовый файл, например JSON или фрагмент JS, который затем используется для отображенияобраз.Однако в этом коде хорошо то, что вам не нужно предоставлять дополнительную информацию.Таким образом, никакие условия гонки, никакие новые странные состояния, как в случае с полным диском, просто не работают.

Итак, одна из основных идей заключается в том, чтобы не изменять код на веб-сервере, который генерирует изображение, просто сделать это на стороне браузера.Таким образом, все, что вам нужно, это мягкая ссылка из дерева веб-страниц на изображение и убедитесь, что изображение атомарно обновлено.

Недостатком AJAX является та же политика происхождения, поэтому AJAX может проверять толькоHEAD ресурсов от хоста, предоставившего работающий код JavaScript.Greasemonkey или Scriptlets могут обойти это, но они не могут быть развернуты для широкой аудитории.Таким образом, к сожалению, нельзя эффективно запрашивать иностранные ресурсы (изображения), обновлены они или нет.С моей стороны, как сценарий, так и изображение, исходят из одного хоста.

Сказав все это, вот проблемы с этим кодом :

Приведенный выше код добавляет задержку. Сначала проверяется ГОЛОВКА, и если это показывает, что что-то изменилось, обновление выполняется. Было бы неплохо сделать и то и другое в одном запросе, чтобы обновление образа не требовало дополнительного туда-обратно.

GET может архивировать это с помощью If-Modified-Since, и это работает на моей стороне, однако я не нашел способа правильно отобразить результат в виде встроенного изображения. Это может работать для некоторых браузеров, но не для всех.

Код также слишком сложен на мой вкус. Вам приходится иметь дело с возможными сетевыми таймаутами, не превышающими ограниченную пропускную способность, пытаться быть дружелюбными к веб-серверу, быть совместимыми с максимально возможным количеством браузеров и т. Д.

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

Возможно, есть неизвестный мне способ "повторно запустить" обновление изображения в браузере? Таким образом, браузер может напрямую проверять If-Modified-Since и обновлять изображение. С JavaScript это может вызвать событие .load тогда или подобное. Со своей стороны, я даже не нуждаюсь в этом, все, что я хочу, это чтобы изображение оставалось актуальным.

Я еще не экспериментировал с CANVAS. Возможно, у кого-то есть идея использовать это.

Так что мой вопрос заключается в том, есть ли лучший способ (другой алгоритм), чем показанный выше, кроме улучшения качества кода?

1 Ответ

0 голосов
/ 19 марта 2011

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

Начните здесь: http://en.wikipedia.org/wiki/Comet_(programming), должен быть простой способ позволить серверу обновить клиента на URL нового образа.Если сервер и клиент поддерживают веб-сокеты, это ярлык.

Однако самое простое решение предполагает отсутствие изменения URL-адреса изображения и запускает

image.src = "";
image.src = url;

с помощью setInterval () и позволяет браузеру работать с кешем.и заголовки.

...