Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

PHP. Хедеры, кэш, jpeg.

32K
11 марта 2010 года
ketzer
10 / / 23.08.2008
Уважаемые, всем доброго времени суток. Совсем запутался, выручайте.
На хостинг, теоритически раз в секунду, грузится jpg, постоянно перезаписывается с темже именем.
Клиент грузит хтмл, где яваскрипт обращается к пхп-сценарию, который получает ETag и выбрасывает ему содержимое jpeg'a, если Etag не совпадает.
Вот коды:
клиентский хтмл
Код:
<HTML>
<HEAD>

<script language="Javascript">
  <!--
 
    var refreshtime=1000;
 
    function refreshCam(){

    document.images["webcam"].src = "test.php"; setTimeout("refreshCam()", refreshtime)
 
 
    }
 

  //-->
</script>
</head>
 
<body>

<img src="test.php" name="webcam" border="0" alt="Изображение загружается..."></center>
<script language="JavaScript">
  <!--
    if( document.images )
    refreshCam();
  //-->
</script>
 
</body>


серверный пхп
Код:
<?php

$filename = ("testcam.jpg");

$fsize=filesize($filename);
$ftime=filemtime($filename);


$request=$_SERVER['HTTP_IF_NONE_MATCH'];
$tag=(fileinode($filename)."-".$fsize."-".$ftime);




if (($request==$tag)  || ($fsize<4000)) {
 header("HTTP/1.1 304 Not Modified");
echo "Cached!";
 exit();
}
       header("Content-Type: image/jpg");
       header( "Cache-Control: must-revalidate" );
       header("ETag: ".$tag);
       header("Last-Modified: ".$ftime);




readfile($filename);
exit();

?>


Вопросов два:
1. этот JS-рефреш работает только в Firefox'e(в опере не пробовал), IE сам не обнавляет картинку, хотя буквально с утра все на нем работало.
2. Он же главный. Переодически при срабатывании рефреша, изображение не загружается, а показывается его alt, с чем это может быть связано? Почему браузер не хватает картинку из кэша, если не может ее загрузить с сервера? Хочу сделать так, чтобы отображалось последнее кэшированное изображение, до тех пор пока не загрузится новейшее изображение с сервера.

Куда не доглядел? Заранее большой рахмад.
253
11 марта 2010 года
Proger_XP
1.5K / / 07.08.2004
На сон глядя вот что могу сказать:
  • document.images["webcam"].src = "test.php";
    Добавляй хоть какой-нибудь random_id туда, например: ... = "test.php?".Math.rand();
    Может поэтому IE и отказывается её грузить.
  • header("Last-Modified: ".$ftime);
    Насколько я помню, в заголовках время никогда не указывается как timestamp, а используется UTC. В PHP для этого кажется gmtime() есть, ну, там в хелпе пример был на эту тему.
32K
11 марта 2010 года
ketzer
10 / / 23.08.2008
Proger_XP, спассибо! Про таймштамп, мне кажется, ты верно указал, я что-то забыл совсем про форматирование, но вот только у меня проверка идет по ETag.

А вот разве приписывание параметра-рандома к ссылке не приведет к ее постоянному обновлению, т.е. броузер ее постоянно будет пытать грузить с сервера, даже не думая о том что у него в кэшэ?

Заранее благодарю за ответы.
253
11 марта 2010 года
Proger_XP
1.5K / / 07.08.2004
Цитата:
Про таймштамп, мне кажется, ты верно указал, я что-то забыл совсем про форматирование, но вот только у меня проверка идет по ETag.


Так дело в том, что неверный формат Last-Modified может заставить браузер вообще не кешировать картинку. На его усмотрение.

Цитата:
А вот разве приписывание параметра-рандома к ссылке не приведет к ее постоянному обновлению?


Гм, ну да. Я бы посоветовал сделать так: в JavaScript'е к test.php добавляешь Date.now(); в PHP вместо статуса 304 делаешь header('Location: test.php?'.$ftime);
В добавок к этому можешь поубирать все 4 заголовка, что идут у тебя после if, оставив только один, который заставит браузер кешировать файл навечно (ну, или на час).

Идея вот в чём: браузер каждую секунду запрашивает картинку по её времени изменения. Затем он её кеширует. При запросе, когда картинка не изменилась, скрипт выдаёт редирект на последнюю версию картинки, которая должна быть уже закеширована браузером.

Хотя кеширование может не везде работать, поэтому, чтобы не редиректить браузер бесконечно, проверяешь переданный timestamp (в $_SERVER['QUERTY_STRING'], например), и если он равен $ftime, то выводишь картинку (как у тебя сейчас после if).

32K
11 марта 2010 года
ketzer
10 / / 23.08.2008
Ага, спасибо.
Цитата:
Гм, ну да. Я бы посоветовал сделать так: в JavaScript'е к test.php добавляешь Date.now(); в PHP вместо статуса 304 делаешь header('Location: test.php?'.$ftime);


подскажи, пожалуйста, а что за JS метод now такой для Date?
Пытался его заменить getTime'мом, так он строку дает длиннее чем filemtime. Только ли дело в том что первый возвращает миллисекунды, а второй секунды?

253
11 марта 2010 года
Proger_XP
1.5K / / 07.08.2004
Цитата:
подскажи, пожалуйста, а что за JS метод now такой для Date?
Пытался его заменить getTime'мом, так он строку дает длиннее чем filemtime. Только ли дело в том что первый возвращает миллисекунды, а второй секунды?


А, now() не стандартный метод. Можно заменить его этим (ref):

 
Код:
Math.round( (new Date()).getTime() / 1000 );

Это вернёт timestamp, как раз то, что используют PHP'шные функции, например, filemtime()
32K
12 марта 2010 года
ketzer
10 / / 23.08.2008
Ага, спасибо, не знал про него, когда наткнулся на это описание, не доперло нажать на метод чтобы прочитать про него))

ВОбщеМ, подправил я код - FF и ёпера работают, а IE не хочет(иногда кажет alt вместо картинки). Покопавшись в RFC2616, понял что походу виноват хедер location, неужели надо лепить проверку браузера и все-таки давить на IE ETag'ом, if-modified-since'ом и last-modified'ом ?

Код:
<?php

$filename = ("testcam.jpg");

@clearstatcache();

$fsize=filesize($filename);
$ftime=filemtime($filename);
$time_mod= date("D, d M Y H:i:s",($ftime)) . " GMT";

if  ($_SERVER['QUERY_STRING'] == $ftime || ($fsize<2000))
{
 header('Location: test.php?'.$_SERVER['QUERY_STRING']);
 exit();
}
       header("Content-Type: image/jpg");
       header( "Cache-Control: public, max-age=1800" );
 //      header("Last-Modified: ".$time_mod);



readfile($filename);

?>


Функция в JS:
[HTML]<script language="Javascript">
<!--

var refreshtime=1000;

function refreshCam()
{

rfsh = "?"+Math.round( (new Date()).getTime() / 1000 );
document.images["webcam"].src = "test.php"+rfsh;
setTimeout("refreshCam()", refreshtime)


}


//-->
</script>[/HTML]

PS Proger_XP, огромное спасибо тебе за хинты. Не ожидал что такой геммор с браузерами, и что есть люди которые помогают плотно))
253
12 марта 2010 года
Proger_XP
1.5K / / 07.08.2004
  • $fsize<2000
    Я всё-таки не совсем понимаю, зачем тут проверка на размер.
  • $_SERVER['QUERY_STRING'] == $ftime
    Должно быть $_SERVER['QUERY_STRING'] >= $ftime - иначе ты будешь выдавать неизменившуюся картинку ($ftime), когда клиент будет просить тебя позже даты её изменения (query string).
  • date("D, d M Y H:i:s",($ftime))
    Я же говорил, что время указывается без сдвига временной зоны - для этого нужно использовать gmdate (с теми же параметрами). В хелпе по PHP есть как раз пример на тему различий date и gmdate.

Впрочем, это всё мелкие недочёты, вряд именно они ломают IE. А когда он показывает alt? Есть какая-то закономерность? Пробовал на IE 8?

Кстати, если у тебя одновременно каждую секунду картинка читается и записывается, то может readfile() читает её как раз в момент записи?
Для блокировки можно использовать flock(), правда, в винде он не работает. Ну, можно свой сэмурировать.

И ещё кое-что: кэширование не ограничивается одним заголовком. Например, для отмены кэширования обычно используют четыре, вроде таких:
 
Код:
header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past
header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); // always modified
header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1
header("Pragma: no-cache"); // HTTP/1.0

Cache-Control у тебя уже есть, остальные три переверни (last modified = gmdate(..., $ftime) и т.д.).

Цитата:
PS Proger_XP, огромное спасибо тебе за хинты. Не ожидал что такой геммор с браузерами, и что есть люди которые помогают плотно))


Всякое бывает :) Кстати, с браузерами гораздо больше мороки (опять же из-за IE) в плане вёрстки и JS, чего в твоём случае не так много.

32K
12 марта 2010 года
ketzer
10 / / 23.08.2008
Цитата:
$fsize<2000


а это если картинка не догрузилась и меньше "стандартного" размера стала, т.е. чтобы часть картинки не показывалась.

Цитата:
Должно быть $_SERVER['QUERY_STRING'] >= $ftime - иначе ты будешь выдавать неизменившуюся картинку ($ftime), когда клиент будет просить тебя позже даты её изменения (query string).


запамятовал, ну вроде и так работает, но впредь учту, спасибо)

Цитата:
Я же говорил, что время указывается без сдвига временной зоны - для этого нужно использовать gmdate (с теми же параметрами). В хелпе по PHP есть как раз пример на тему различий date и gmdate.


gmdate был раьнше, date остался после экспериментов. не понимаю, сервак пишет Date: Fri, 12 Mar 2010 20:12:08 GMT, хотя должно быть на 3 часа позже. пример с финляндией, я уже видел, и что-то он меня не впечатлил))

Цитата:
И ещё кое-что: кэширование не ограничивается одним заголовком. Например, для отмены кэширования обычно используют четыре, вроде таких:


так разве я тут не пытаюсь боротся, наоборот, за включение кэширования? Да и заголовки эти , ранее, я уже прикручивал...не айс(

253
13 марта 2010 года
Proger_XP
1.5K / / 07.08.2004
Цитата:
а это если картинка не догрузилась и меньше "стандартного" размера стала, т.е. чтобы часть картинки не показывалась.


Так ты подумай, что у тебя скрипт будет делать - он будет бесконечно редиректить браузер сам на себя. Да и потом, проверку на размер нужно не тут производить наверно, а в клиенте, который закачивает эту картинку. Например, пишет её в один файл (временный), а потом при удачной закачке переписывает её в другой файл (testcam.jpg у тебя).
Тогда частичная загрузка тебе не грозит.

Цитата:
запамятовал, ну вроде и так работает, но впредь учту, спасибо)


Иначе пропадает весь смысл в кешировании...

Цитата:
mdate был раьнше, date остался после экспериментов. не понимаю, сервак пишет Date: Fri, 12 Mar 2010 20:12:08 GMT, хотя должно быть на 3 часа позже


Наоборот - если GMT + 3 часа по Москве, то gmdate() тебе пишет всё правильно - на три часа раньше. В заголовках указывается время по Гринвичу, без всяких добавлений времянных зон - иначе как разруливать, когда браузер в одной части земного шара, а сервер - в другой?

Цитата:
так разве я тут не пытаюсь боротся, наоборот, за включение кэширования?


Я же написал:

Цитата:
Cache-Control у тебя уже есть, остальные три переверни


Речь о том, что если для отмены кеширования нужны 4 заголовка, то, логично, для его нормального включения нужны тоже 4 заголовка.

32K
13 марта 2010 года
ketzer
10 / / 23.08.2008
Цитата:
Я же написал:


блин, прочитал не пЕреверни, а прИверни)))

Цитата:
Речь о том, что если для отмены кеширования нужны 4 заголовка, то, логично, для его нормального включения нужны тоже 4 заголовка.


логично!:)

изменил, IE все-равно тупит..

Код:
<?php

$filename = ("testcam.jpg");

@clearstatcache();

$fsize=filesize($filename);
$ftime=filemtime($filename);
$time_mod= gmdate("D, d M Y H:i:s",($ftime)) . " GMT";

if  ($_SERVER['QUERY_STRING'] >= $ftime || ($fsize<5000))
{
 header('Location: test.php?'.$_SERVER['QUERY_STRING']);
 exit();
}
       header("Content-Type: image/jpg");
       header( "Cache-Control: public, max-age=1800" );
       header("Expires:".gmdate("D, d M Y H:i:s",($ftime+1800)) . " GMT");
       header("Last-Modified: " . gmdate("D, d M Y H:i:s",$ftime)." GMT");
       header("Pragma: cache");

readfile($filename);

?>


Кстати, проверка размера файла мне кажется удачным решением. Ведь ты же сам говорил что редирект идет на кэш, а потому картинка будет постоянно на экране(должна быть), а если сделать ее обновление ~0,1сек, то я с большой долей вероятности избегаю попадания ридфайла во время загрузки картинки на сервер. По крайней мере, выглядит в ФФ и ёпере все неплохо. А IE вообще плюет на кэш, и выводит альт через раз загрузки jpeg'a.

кстати, вот резуль сервера на скрипт:
Server: nginx/0.4.13
Date: Sat, 13 Mar 2010 07:58:30 GMT
Content-Type: image/jpg
Cache-Control: public, max-age=1800
Expires: Sat, 13 Mar 2010 08:28:29 GMT
Pragma: cache
X-Powered-By: PHP/4.4.4-8+etch6
Last-Modified: Sat, 13 Mar 2010 07:58:29 GMT

200 OK

Видимо по 200 коду он и не думает в кэш глядеть, онж ему постоянно приходитО_о

Если появтся идеи, буду рад, а пока буду шаманить далее. Спасибо!
253
13 марта 2010 года
Proger_XP
1.5K / / 07.08.2004
Цитата:
блин, прочитал не пЕреверни, а прИверни)))


Это бывает :D

Цитата:
изменил, IE все-равно тупит..


Честно говоря, не удивляет...

Цитата:
"код"


С кодом вроде всё ок, правда один мометнт - не понятно, зачем тебе $time_mod: во-первых, неиспользуешь, во-вторых, я бы не стал её вообще определять, раз уж один раз может использоваться (подразумевается Last-Modified). Ну это дело вкуса.

Цитата:
Ведь ты же сам говорил что редирект идет на кэш, а потому картинка будет постоянно на экране(должна быть),


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

Я бы использовал любой из двух подходов, которые уже описывал (flock() или использование одного временного файла и подмена им настоящего по завершению загрузки).

Ну или, если уж делать по-твоему, то по крайней мере имеет смысл поменять учасок с условием так:

 
Код:
if  ($_SERVER['QUERY_STRING'] >= $ftime || ($fsize<5000))
{
 header('Location: test.php?'.($ftime - 1));
 exit();
}

Здесь браузер редиректится на реально последнее время изменение файла (а не QS, которая может быть хоть 100 часов в будущем), и минус 1 секунда, чтобы браузер мог использовать кеш, который он запомнил при запросе предыдущего кадра.

Цитата:
Видимо по 200 коду он и не думает в кэш глядеть, онж ему постоянно приходитО_о


Да нет, по идее (по стандарту) всё правильно - код 200 как раз нормальный показатель, что страницу можно кешировать. Впрочем, кто его знает, что в голове у UE...
Так ты пробовал это в 8 версии оного?

Цитата:
Если появтся идеи, буду рад, а пока буду шаманить далее. Спасибо!


Хм, ну честно говоря эта идея (с кешированием) выглядит наиболее простой. Правда, если уж так глухо с IE, то можно повозиться над клиентской частью - JS то есть. Например, ты можешь сделать интерфейс вроде такого:

  • test.php?action=isnewframe&since=timestamp - скрипт должен вернуть либо только 0 - кадр старый, либо 1 - появился новый кадр. timestamp - последний кадр, который JS запрашивал (и запомнил, соответственно).
  • test.php?action=lastframe - сервер всегда возвращает само содержимое кадра (картинку). Грубо говоря, можно даже не скрипт запрашивать, а сразу статическую картинку (посколько скрипт будет всегда редиректить на неё, если не придумаешь каких-то дополнительных проверок/действий).
Таким образом, JS у клиента раз в секунду запрашивает сервер - isnewfram, и только при ответе "1" он перезагружает картинку (ставит её src в action=lastframe& + Math.rand()). Ну а при ответе 0 он ничего не делает.
Поэтому как бы криво браузер не располряжался кешированием тут ему ничего не сделать, так как этим занимается сам скрипт.

Как запрашивать сервер ты уж точно найдёшь кроссбраузерный способ - например, iframe или XMLHttpRequest.

При таком подходе, конечно, нагрузка на сервер часто (зависит от частоты обновления кадра) будет в 2 раза выше, чем при подходе, который мы пытались сделать до сих пор.
Но по крайней мере это должно работать даже в IE.
32K
15 марта 2010 года
ketzer
10 / / 23.08.2008
Ох, блин, буду разбираться. Спасибо за пищу для размышлений. Если что, апну тему.
Proger_XP, огромное спасибо за уделенное время.
253
15 марта 2010 года
Proger_XP
1.5K / / 07.08.2004
Цитата: ketzer
Ох, блин, буду разбираться. Спасибо за пищу для размышлений. Если что, апну тему.
Proger_XP, огромное спасибо за уделенное время.


Без проблем :)

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог