在PHP脚本中处理If-modified-since头部信息

14

我有一个PHP脚本,它通过?img=参数调用。

该参数的值是一个被URL编码的图片URL。

我的脚本检查是否已经在我的服务器上存储了该图片。

如果没有-它会下载它。之后,它可选地调整图像大小并将其发送到STDOUT,即返回给请求浏览器,附加了Content-TypeLast-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

为了解决一些跨域问题,我需要这个方法。在过去一年中,它为我提供了很好的效果:

screenshot

但是现在我想添加处理If-Modified-since头的功能 - 发送一个Not Modified 304响应。

我的问题如下:

1)在Apache中运行的PHP是否可以实现这一点?

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);
}

?>
2个回答

43

在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"标头并停止。

否则,脚本将按照正常方式继续执行。


1
如果有帮助的话,我写了一个解析HTTP日期的解析器,它可以验证并覆盖所有三种格式:https://github.com/evert/SabreDAV/blob/master/lib/Sabre/HTTP/Util.php#L23 - Evert
1
谢谢大家,这个方法可行,我选择比较字符串而不是解析日期 - 在我的情况下似乎效果很好: $mod = gmdate('D, d M Y H:i:s T', filemtime($cached)); if (@$_SERVER['HTTP_IF_MODIFIED_SINCE'] == $mod) { header('HTTP/1.0 304 Not Modified'); exit; } - Alexander Farber
@Ja͢ck,我如何在Bash或C99中做到相同的事情? - user2284570

7

最近我需要使用这个功能(通过PHP提供图像)以便仅注册用户可以查看图片。Jack的代码很有帮助,但是我需要做一些修改才能让它完美运行。我想分享一下我的修改。

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);
}

简单来说,如果是浏览器请求 HTTP_IF_MODIFIED_SINCE,则该脚本返回未修改内容。否则,图像将以适当的标头和到期日期提供服务。

3
不要将整个图像文件读入内存,使用readfile($path_to_image)将其流式传输到浏览器。 - JasonWoof

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接