使用 PHP 和 MySQL 缓存调整大小的图像的最佳方法
题
使用 PHP 处理图像缓存的最佳实践方法是什么?
文件名当前存储在 MySQL 数据库中,该数据库在上传时与原始文件名和 alt 标记一起重命名为 GUID。
当图像被放入 HTML 页面时,使用诸如“/images/get/200x200/{guid}.jpg”之类的 url 来完成,该 url 被重写为 php 脚本。这允许我的设计师指定(粗略地 - 源图像可能更小)文件大小。
然后,php 脚本创建大小(url 中为 200x200)和 GUID 文件名的散列,如果之前已生成该文件(TMP 目录中存在具有散列名称的文件),则从应用程序 TMP 目录发送该文件。如果散列文件名不存在,则会创建该文件名,将其写入磁盘并以相同的方式提供服务,
这是否有效?(它还支持对图像加水印,并且水印设置也存储在哈希中,但这超出了范围。)
解决方案
Dan Udey 的重写示例中有两个拼写错误(我无法对此发表评论),它应该是:
RewriteCond %{REQUEST_URI} ^/images/cached/
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI} !-f
RewriteRule (.*) /images/generate.php?$1 [L]
问候。
其他提示
我会用不同的方式来做。
问题:1.让 PHP 提供文件的效率低于其应有的水平。2.每次请求图像3时,PHP必须检查文件的存在3。Apache 在这方面比 PHP 做得更好。
这里有一些解决方案。
您可以使用 mod_rewrite
在阿帕奇上。可以使用 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_expires
, 、Last-Modified 标头等。响应有条件的 GET 请求。如果客户端不必重新请求图像,页面加载速度将显着加快,服务器上的负载也会减少。
对于必须从 PHP 发送图像的情况,您可以使用 mod_xsendfile
以更少的开销做到这一点。看 阿诺德·丹尼尔斯的精彩博客文章 关于这个问题,但请注意他的示例是用于下载的。要内联提供图像,请取出 Content-Disposition 标头(第三个 header() 调用)。
希望这会有所帮助——在我的偏头痛消失后会有更多帮助。
值得添加的一个注释是确保您的代码不会生成这些图像的“未经授权”大小。
因此,以下 URL 将创建图像 1234 的 200x200 版本(如果尚不存在)。ID 高度 建议您确保请求的 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
}
?>
看起来很棒的帖子,但我的问题仍然没有解决。我无法访问我的主机提供商中的 htaccess,因此不存在 apache 调整的问题。真的有办法为图像设置 cace-control 标头吗?
您的方法似乎相当合理 - 我想补充一点,应该采取某种机制来检查缓存版本的生成日期是否在原始(源)图像文件的最后修改时间戳之后,如果不重新生成缓存/调整大小的版本。这将确保如果设计者更改图像,缓存将得到适当更新。
这听起来是一个可靠的方法。下一步可能会超越 PHP/MySQL。
也许, 调整你的标题:
如果您使用 PHP 发送 MIME 类型,您还可以使用“Keep-alive”和“Cache-control”标头来延长服务器上图像的寿命并减轻 PHP/MySQL 的一些负载。
另外,还可以考虑使用 apache 插件进行缓存。喜欢 mod_expires.
哦,还有一件事,你对你的服务器有多少控制权?我们是否应该将这次谈话限制为 只是 PHP/MySQL?
我已经成功地使用 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");
}
我不喜欢将文件地址保留在数据库中,而是更喜欢在用户登录时向文件名添加随机数。对于用户 1234 来说是这样的:图片/picture_1234.png?rnd=6534122341
如果用户在会话期间提交新图片,我只需刷新随机数。
GUID 100% 解决了缓存问题。然而,这使得跟踪图片文件变得更加困难。使用此方法,用户有可能在将来登录时再次看到相同的图片。然而,如果您从十亿个数字中生成随机数,则几率很低。
phpThumb 是一个可以动态生成调整大小的图像/缩略图的框架。它还实现了缓存并且非常容易实现。
调整图像大小的代码是:
<img src="/phpThumb.php?src=/path/to/image.jpg&w=200&h=200" alt="thumbnail"/>
将为您提供 200 x 200 的缩略图;
它还支持水印。