14

使用 PHP 处理图像缓存的最佳实践方法是什么。

文件名当前存储在 MySQL 数据库中,该数据库在上传时重命名为 GUID,以及原始文件名和 alt 标签。

当图像被放入 HTML 页面时,它是使用 '/images/get/200x200/{guid}.jpg 之类的 url 完成的,它被重写为 php 脚本。这允许我的设计人员指定(大致 - 源图像可能更小)文件大小。

然后,php 脚本会创建一个大小为 200x200 的哈希值和 GUID 文件名,如果该文件之前已生成(TMP 目录中存在具有该哈希值的文件),则从应用程序 TMP 目录发送该文件。如果散列文件名不存在,那么它会被创建,写入磁盘并以相同的方式提供,

这是否有效?(它还支持对图像进行水印处理,并且水印设置也存储在哈希中,但这超出了此范围。)

4

9 回答 9

30

我会以不同的方式来做。

问题: 1. 让 PHP 提供文件的效率低于其应有的效率。2. PHP 必须在每次请求图像时检查文件是否存在 3. Apache 在这方面比 PHP 做得更好。

这里有一些解决方案。

您可以mod_rewrite在 Apache 上使用。可以使用 mod_rewrite 来测试文件是否存在,如果存在,则改为提供该文件。这完全绕过了 PHP,并使事情变得更快。但是,真正做到这一点的方法是生成一个应该始终存在的特定 URL 模式,如果不存在则重定向到 PHP。

例如:

RewriteCond %{REQUEST_URI} ^/images/cached/
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
RewriteRule (.*) /images/generate.php?$1 [L]

因此,如果客户端请求/images/cached/<something>并且该文件不存在,Apache 会将请求重定向到/images/generate.php?/images/cached/<something>. 这个脚本然后可以生成图像,将其写入缓存,然后将其发送到客户端。将来,除了新图像外,永远不会调用 PHP 脚本。

使用缓存。正如另一位发帖人所说,使用诸如mod_expiresLast-Modified 标头之类的东西来响应条件 GET 请求。如果客户端不必重新请求图像,页面加载速度将显着加快,服务器上的负载将减少。

对于必须从 PHP 发送图像的情况,您可以使用mod_xsendfile更少的开销来完成它。请参阅Arnold Daniels 关于该问题的出色博客文章,但请注意他的示例是用于下载的。要内联提供图像,请取出 Content-Disposition 标头(第三个 header() 调用)。

希望这会有所帮助——在我的偏头痛痊愈后会有所帮助。

于 2008-09-26T19:17:48.357 回答
10

Dan Udey 的重写示例中有两个错别字(我无法对此发表评论),应该是:

RewriteCond %{REQUEST_URI} ^/images/cached/
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
RewriteRule (.*) /images/generate.php?$1 [L]

问候。

于 2009-12-29T15:02:58.823 回答
5

值得添加的一个注意事项是确保您的代码不会生成这些图像的“未经授权”大小。

因此,如果图像 1234 尚不存在,则以下 URL 将创建 200x200 版本的图像。我强烈建议您确保请求的 URL 包含您支持的图像尺寸。

/images/get/200x200/1234.jpg

恶意的人可以开始请求随机 URL,总是改变图像的高度和宽度。这会导致您的服务器出现一些严重的问题,因为它会坐在那里,基本上受到攻击,生成您不支持的大小的图像。

/images/get/0x1/1234.jpg
/images/get/0x2/1234.jpg
...
/images/get/0x9999999/1234.jpg
/images/get/1x1/1234.jpg
...
etc

这是说明这一点的随机代码片段:

<?php

    $pathOnDisk = getImageDiskPath($_SERVER['REQUEST_URI']);

    if(file_exists($pathOnDisk)) {
        // send header with image mime type 
        echo file_get_contents($pathOnDisk);
        exit;
    } else {
        $matches = array();
        $ok = preg_match(
            '/\/images\/get\/(\d+)x(\d+)\/(\w+)\.jpg/', 
            $_SERVER['REQUEST_URI'], $matches);

        if(! $ok) {
            // invalid url
            handleInvalidRequest();
        } else {
            list(, $width, $height, $guid) = $matches;

            // you should do this!
            if(isSupportedSize($width, $height)) {
                // size is supported. all good
                // generate the resized image, save it & output it
            } else {
                // invalid size requested!!!
                handleInvalidRequest();
            }
        }
    }

    // snip
    function handleInvalidRequest() {
        // do something w/ invalid request          
        // show a default graphic, log it etc
    }
?>
于 2008-09-26T18:48:14.403 回答
1

似乎很棒的帖子,但我的问题仍然没有解决。我无法访问我的主机提供商中的 htaccess,所以没有 apache 调整的问题。真的有办法为图像设置 cace-control 标头吗?

于 2009-03-01T23:51:23.893 回答
0

您的方法似乎很合理 - 我要补充一点,应该采取一些机制来检查生成缓存版本的日期是否在原始(源)图像文件的最后修改时间戳之后,如果不重新生成缓存/调整大小的版本. 这将确保如果设计者更改了图像,缓存将得到适当的更新。

于 2008-09-26T17:22:09.147 回答
0

这听起来像是一个可靠的方法。下一步可能是超越 PHP/MySQL。

也许,调整你的标题

如果您使用 PHP 发送 MIME 类型,您还可以使用“Keep-alive”和“Cache-control”标头来延长图像在服务器上的寿命并减轻 PHP/MySQL 的一些负载。

此外,还要考虑使用 apache 插件进行缓存。像mod_expires

哦,还有一件事,你对你的服务器有多少控制权?我们是否应该将此对话限制PHP/MySQL 上?

于 2008-09-26T17:29:20.240 回答
0

phpThumb是一个动态生成调整大小的图像/缩略图的框架。它还实现了缓存,而且很容易实现。

调整图像大小的代码是:

<img src="/phpThumb.php?src=/path/to/image.jpg&w=200&amp;h=200" alt="thumbnail"/>

将为您提供 200 x 200 的缩略图;

它还支持水印。

看看: http: //phpthumb.sourceforge.net/

于 2008-09-26T18:34:26.043 回答
0

我已经设法在 PHP 中简单地使用重定向标头来做到这一点:

if (!file_exists($filename)) {  

    // *** Insert code that generates image ***

    // Content type
    header('Content-type: image/jpeg'); 

    // Output
    readfile($filename);    

} else {
    // Redirect
    $host  = $_SERVER['HTTP_HOST'];
    $uri   = rtrim(dirname($_SERVER['PHP_SELF']), '/\\');
    $extra = $filename;
    header("Location: http://$host$uri/$extra");
}
于 2009-03-05T17:01:28.170 回答
0

我宁愿在用户登录时在文件名中添加一个随机数,而不是将文件地址保留在数据库中。对于用户 1234,类似这样的内容:image/picture_1234.png?rnd=6534122341

如果用户在会话期间提交了新图片,我只需刷新随机数。

GUID 100% 解决缓存问题。但是,它有点难以跟踪图片文件。使用这种方法,用户有可能在以后登录时再次看到相同的图片。但是,如果您从十亿个数字中生成随机数,则几率很低。

于 2010-04-04T00:18:37.180 回答