【问题标题】:Best way to cache resized images using PHP and MySQL使用 PHP 和 MySQL 缓存调整大小图像的最佳方法
【发布时间】:2010-09-13 12:49:11
【问题描述】:

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

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

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

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

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

【问题讨论】:

    标签: php image caching resize


    【解决方案1】:

    我会以不同的方式来做。

    问题: 1. 让 PHP 提供文件的效率低于预期。 2. 每次请求图像时,PHP 都必须检查文件是否存在 3. Apache 在这方面比 PHP 要好得多。

    这里有一些解决方案。

    您可以在 Apache 上使用 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 以减少开销。请参阅the excellent blog post from Arnold Daniels 关于该问题,但请注意他的示例是用于下载的。要内联提供图像,请取出 Content-Disposition 标头(第三个 header() 调用)。

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

    【讨论】:

    • 精湛的方法谢谢。结合 CakePHP 路由,非常优雅的解决方案。
    • 优秀的方法,正是我现在需要的。我希望我能投票两次。
    【解决方案2】:

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

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

    问候。

    【讨论】:

    • 谢谢,我已经更新了我的代码,以防他们使用它而不是你的固定版本。
    【解决方案3】:

    值得补充的一点是确保您的代码不会生成这些图像的“未经授权”尺寸。

    因此,如果图像 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
        }
    ?>
    

    【讨论】:

    • 使用 die() 是可怕的代码。您基本上是退出,将单行文本转储到客户端,而不进行任何错误更正。如果此示例在生产中使用,请将 die() 调用替换为替换图像或有用的错误。
    • 我同意你的看法。它只是简单的示例代码
    • 这已经在我的代码中实现(如果出现任何问题,最大和最小图像大小带有占位符图像)。赞成,因为考虑 PHP 安全性总是好的!
    【解决方案4】:

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

    【讨论】:

      【解决方案5】:

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

      【讨论】:

      • 好建议,但是...如果源被上传的新图像(通过管理界面)更改,则生成新的 GUID 并且缓存的文件名将不再匹配。图片不应该使用 (s)FTP 上传,但我们都知道假设。 ;) 无论如何都会考虑实施。
      • 我还检查图像是否在对缓存脚本进行任何修改后被缓存,即if (filemtime($cached_image)&gt;filemtime($_SERVER['SCRIPT_FILENAME'])) {...}
      【解决方案6】:

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

      也许,调整你的标题

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

      此外,还可以考虑使用 apache 插件进行缓存。喜欢mod_expires

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

      【讨论】:

      • 该应用程序托管在 VPS 上,因此我具有 root 访问权限。 mod_expires 已安装,所以看起来一个简单的 .htaccess 调整可以稍微减少服务器负载。很好的提示,谢谢。
      【解决方案7】:

      我已经设法在 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");
      }
      

      【讨论】:

        【解决方案8】:

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

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

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

        【讨论】:

          【解决方案9】:

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

          调整图片大小的代码是:

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

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

          它还支持水印。

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

          【讨论】:

            猜你喜欢
            • 2019-11-13
            • 1970-01-01
            • 2021-01-29
            • 2013-07-26
            • 1970-01-01
            • 2012-05-11
            • 1970-01-01
            • 1970-01-01
            • 2015-08-28
            相关资源
            最近更新 更多