Обработка заголовка If-modified-since в PHP- script

У меня есть PHP script, который вызывается с параметром ? img =.

Значение для этого параметра - URL (urlencoded) изображения.

Мой script проверяет, если это изображение уже сохранено на моем сервере.

Если нет - он загружает его. После этого он необязательно изменяет размер изображения и отправляет его в STDOUT, то есть обратно в запрашивающий браузер, с добавлением заголовков Content-Type и Last-modified:

Connection:close
Content-Type:image/jpeg
Date:Fri, 01 Jun 2012 08:28:30 GMT
Last-Modified:Fri, 01 Jun 2012 08:02:44 GMT
Server:Apache/2.2.15 (CentOS)
Transfer-Encoding:chunked
X-Powered-By:PHP/5.3.3

Это необходимо для решения некоторых проблем Flash/Crossdomain.xml и хорошо работает для меня с года:

enter image description here

Однако я хотел бы добавить функциональность для обработки входящего заголовка If-Modified-since, чтобы отправить ответ Not Modified 304.

Мои вопросы:

1) Возможно ли это даже в PHP, когда выполняется в Apache?

2) Как обрабатывать (то есть анализировать и производить) лучшие даты в PHP здесь?

Бонусный вопрос) Как добавить заголовок Content-Length для измененных изображений?

Мой код ниже (я пропустил часть загрузки CURL):

<?php

define('CACHE_DIR', '/var/www/cached_avatars/');

$img    = urldecode($_GET['img']);
$cached = CACHE_DIR . md5($img);

# omitted downloading part for brevity

$readfh = fopen($cached, 'rb');
if ($readfh) {
        flock($readfh, LOCK_SH);

        $size = getimagesize($cached);
        $w    = $size[0];
        $h    = $size[1];
        $type = $size[2];
        $mime = $size['mime'];

        # find the downscale factor to fit image into $maxw x $maxh
        $scale = max($w / $maxw, $h / $maxh);

        header('Content-Type: ' . $size['mime']);
        header('Last-Modified: ' . gmdate('D, d M Y H:i:s T', filemtime($cached)));

        $length = filesize($cached);
        $buf = fread($readfh, $length);
        fclose($readfh);

        # the image is smaller than $maxw x $maxh, do not scale up
        if ($scale <= 1) {
                header('Content-Length: ' . $length);
                print($buf);
                return;
        }

        $tw = $w / $scale;
        $th = $h / $scale;
        $image = imagecreatefromstring($buf);
        $thumb = imagecreatetruecolor($tw, $th);
        imagecopyresampled($thumb, $image, 0, 0, 0, 0, $tw, $th, $w, $h);
        imagedestroy($image);

        # How to add Content-Length here, after image resizing?

        if (IMAGETYPE_JPEG == $type)
                imagejpeg($thumb, null, 75);
        else if (IMAGETYPE_PNG == $type)
                imagepng($thumb, null, 9);
        else if (IMAGETYPE_GIF == $type)
                imagegif($thumb, null);

        imagedestroy($thumb);
}

?>

Я не прошу, чтобы кто-нибудь выполнял мою работу за меня, просто просил о хороших указателях или фрагментах кода, поскольку у меня нет опыта в этих областях (заголовки + даты) PHP еще.

Ответ 1

Это возможно в PHP!

Когда браузер проверяет наличие изменений, он отправляет заголовок If-Modified-Since; в PHP это значение будет установлено внутри $_SERVER['HTTP_IF_MODIFIED_SINCE'].

Чтобы декодировать значение даты/времени (например, закодированное с помощью rfc822), вы можете просто использовать strtotime(), поэтому:

if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && 
    strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= filemtime($localFileName))
{
    header('HTTP/1.0 304 Not Modified');
    exit;
}

Объяснение: если заголовок If-Modified-Since отправляется браузером, а дата/время - это, по крайней мере, измененная дата файла, который вы обслуживаете, вы пишете заголовок "304 Not Modified" и останавливаетесь.

В противном случае script продолжается как обычно.

Ответ 2

Недавно мне пришлось использовать эту функцию (показ изображения через PHP), чтобы сделать изображения видимыми только для зарегистрированных пользователей. Джек был полезен, но мне пришлось сделать несколько хаков, чтобы он работал отлично. Думаю, я должен поделиться этим.

if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && 
    strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']) >= filemtime($path_to_image))
{
    header('HTTP/1.0 304 Not Modified');
    header("Cache-Control: max-age=12096000, public");
    header("Expires: Sat, 26 Jul 2015 05:00:00 GMT");
    header("Pragma: cache");
    exit;
}else{
    header("Content-type: image/jpeg");
    header("Cache-Control: max-age=12096000, public");
    header("Expires: Sat, 26 Jul 2015 05:00:00 GMT");
    header("Pragma: cache");
    echo file_get_contents($path_to_image);
}

Короче говоря, script возвращает Not Modified, если это запрос браузера HTTP_IF_MODIFIED_SINCE. В противном случае изображение подается с соответствующими заголовками и датами истечения срока действия.