通过动态调整大小服务图像

时间:2010-02-10 14:06:05

标签: image performance webserver image-resizing

我公司最近开始为我们网站的图像处理遇到问题。

我们有几个网站(成人娱乐),可以显示像DVD封面,快照和类似的图像。我们有大约100,000部电影,每部电影我们平均有30部快照+封面。几乎每个图像都有一个附加版本,模糊和覆盖非成员,这导致每部电影约50个图像或总共500万个基本图像。每个图像都有多个版本,具体取决于它放置在页面上的位置(缩略图,原始,小预览,不太小的预览,顶部列表中的小图像等),这会产生比图像更多的图像。我很想数数。

现在我有了使用服务器即时生成图像的想法,因为为所有不同的页面生成所有不同的图像变得非常笨拙(因为不同的页面有时甚至需要不同的图像大小,基本上同样的任务)。

有没有人知道图像处理服务器可以动态缩小图像,所以我们只需要提供原始图像,网络人员只需要他们需要的任何尺寸?

要求:

  • 性能非常高(每天数千名用户)
  • 即时模糊和叠加创建
  • 动态调整大小(有和没有保持宽高比)
  • 可以处理数百万张图片
  • 必须能够阅读JPG,GIF,PNG和BMP并在它们之间进行转换

安全性不是那么令人担忧,因为URL操作已经可以达到不模糊的图像,而且更安全性会很好但不是必需的,坦率地说我停止了关注(在未能进入我的同事头之后为什么(对于我们的小型经销商页面)使用http://example.com/view_image.php?filename=/data/images/01020304.jpg来显示图片是一个坏主意。

我们尝试使用PHP脚本来执行此操作,但对于这么多用户来说,性能太慢了。

提前感谢您提出的任何建议。

8 个答案:

答案 0 :(得分:27)

我建议您设置一个专用的Web服务器来处理图像大小调整并提供最终结果。我做了类似的事情,虽然规模要小得多。它基本上消除了检查缓存的过程。

它的工作原理如下:

  • 您请求将所需尺寸附加到文件名中的图片,例如http://imageserver/someimage.150x120.jpg
  • 如果图像存在,则返回时不进行其他处理(这是主要点,缓存检查是隐式的)
  • 如果图像不存在,则通过.htaccess处理未找到的404,并将请求重新路由到生成所需大小图像的脚本
  • 在脚本中指定允许的大小列表,以避免攻击请求每个可能大小的脚本关闭服务器
  • 将此保留在无Cookie域上以最大限度地减少不必要的流量
编辑:我不认为PHP本身会使这个过程变慢,因为在这种情况下PHP脚本减少到最小:图像缩放是由用C编写的内置库完成的。无论你做什么,你都会必须使用像这样的库(GD或libmagick等),这是不可避免的。至少在你的系统中你完全省去了检查缓存的开销,从而进一步减少了PHP的交互。您可以在现有服务器上实现此功能,因此我认为这是一个非常适合您预算的解决方案。

答案 1 :(得分:7)

基于

  

我们尝试使用PHP脚本来执行此操作,但对于这么多用户来说,性能太慢了。

我会假设你没有缓存结果。我建议将生成的图像缓存一两天(例如,让脚本检查缩略图是否已经生成,如果是,请使用它,如果它还没有动态生成)。

这会大大提高性能,因为我认为主页面/起始页面可能比随机视频X有更多的点击量,因此在查看主页面时,不会在缓存时创建图像。当用户Y观看电影X时,他们不会注意到延迟,因为它只需要生成那一页。

对于“即时调整大小”方面 - 带宽对您有多重要?我想假设你经历了很多关于电影的事情,每次请求中额外的kb图像不会造成太大的伤害。如果是这种情况,您可以使用更大的图像并设置宽度和高度,让浏览器为您进行缩放。

答案 2 :(得分:4)

来自 Drupal 社区的ImageCacheImage Exact Sizes解决方案可能会这样做,并且像大多数解决方案一样,OSS使用ImageMagik中的库

Amazons EC2服务有一些AMI图像可以进行图像缩放。它使用Amazon S3进行图像存储,原始和缩放,并可以将它们提供给Amazons CDN服务(Cloud Front)。在EC2网站上查看可用的内容

另一种选择是谷歌。 Google文档现在支持所有文件类型,因此您可以将图像加载到Google文档文件夹,并共享该文件夹以供公共访问。 URL很长,例如。

http://lh6.ggpht.com/VMLEHAa3kSHEoRr7AchhQ6HEzHVTn1b7Mf-whpxmPlpdrRfPW216UhYdQy3pzIe4f8Q7PKXN79AD4eRqu1obC7I

添加= s参数来缩放图像,很酷!例如200像素宽

http://lh6.ggpht.com/VMLEHAa3kSHEoRr7AchhQ6HEzHVTn1b7Mf-whpxmPlpdrRfPW216UhYdQy3pzIe4f8Q7PKXN79AD4eRqu1obC7I=s200

Google仅收取5美元/年的20GB。有一个完整的API用于上传文档等

关于SO的其他答案 How best to resize images off-server

答案 3 :(得分:2)

好的第一个问题是用任何语言调整图像大小需要一点处理时间。那你如何支持成千上万的客户呢?我们会将其缓存,因此您只需生成一次图像。下次有人要求该图像时,检查它是否已经生成,如果它刚刚返回该图像。如果您有多个应用服务器,那么您需要缓存到中央文件系统,以提高缓存命中率并减少所需的空间量。

为了正确缓存,您需要使用可预测的命名约定,该约定考虑了您希望图像显示的所有不同方式,即使用类似myimage_blurred_320x200.jpg的内容来保存已模糊并调整为300的jpeg宽度和200高度等。

另一种方法是将您的图像服务器放在代理服务器后面,这样就可以自动为您完成所有缓存逻辑,并且您的图像由快速的本机Web服务器提供。

您无法以任何其他方式提供数百万个已调整大小的图像。谷歌和Bing地图就是这样做的,他们在不同的预设范围内预先生成他们需要的所有图像,这样他们就可以提供足够的性能并能够返回预先生成的静态图像。

如果php太慢,您应该考虑使用Java或.NET中的2D图形库,因为它们非常丰富并且可以支持您的所有要求。为了获得Graphics API的一种风格,.NET中的一种方法是将任何图像的大小调整为指定的新宽度或高度。如果省略高度或宽度,它将调整大小以保持正确的宽高比。注意图像可以是从JPG,GIF,PNG或BMP创建的:

// Creates a re-sized image from the SourceFile provided that retails the same aspect ratio of the SourceImage. 
// -    If either the width or height dimensions is not provided then the resized image will use the 
//      proportion of the provided dimension to calculate the missing one.
// -    If both the width and height are provided then the resized image will have the dimensions provided 
//      with the sides of the excess portions clipped from the center of the image.
public static Image ResizeImage(Image sourceImage, int? newWidth, int? newHeight)
{
    bool doNotScale = newWidth == null || newHeight == null; ;

    if (newWidth == null)
    {
        newWidth = (int)(sourceImage.Width * ((float)newHeight / sourceImage.Height));
    }
    else if (newHeight == null)
    {
        newHeight = (int)(sourceImage.Height * ((float)newWidth) / sourceImage.Width);
    }

    var targetImage = new Bitmap(newWidth.Value, newHeight.Value);

    Rectangle srcRect;
    var desRect = new Rectangle(0, 0, newWidth.Value, newHeight.Value);

    if (doNotScale)
    {
        srcRect = new Rectangle(0, 0, sourceImage.Width, sourceImage.Height);
    }
    else
    {
        if (sourceImage.Height > sourceImage.Width)
        {
            // clip the height
            int delta = sourceImage.Height - sourceImage.Width;
            srcRect = new Rectangle(0, delta / 2, sourceImage.Width, sourceImage.Width);
        }
        else
        {
            // clip the width
            int delta = sourceImage.Width - sourceImage.Height;
            srcRect = new Rectangle(delta / 2, 0, sourceImage.Height, sourceImage.Height);
        }
    }

    using (var g = Graphics.FromImage(targetImage))
    {
        g.SmoothingMode = SmoothingMode.HighQuality;
        g.InterpolationMode = InterpolationMode.HighQualityBicubic;

        g.DrawImage(sourceImage, desRect, srcRect, GraphicsUnit.Pixel);
    }

    return targetImage;
}

答案 4 :(得分:1)

在提出这个问题的时候,一些公司如雨后春笋般涌现出来处理这个问题。这不是一个与您或您的公司隔离的问题。许多公司达到了他们需要为图像处理需求寻找更永久解决方案的程度。

imgix这样的服务充当代理和CDN,用于图像操作,例如调整大小和应用叠加层。通过操作URL,您可以对每个图像应用不同的转换。 imgix每天提供数十亿的请求。

您也可以自己提供服务并将其置于CDN之后。像imageproxy这样的开源项目对此有好处。这会给您的运营团队带来维护负担。

(免责声明:我为imgix工作。)

答案 5 :(得分:1)

你正在寻找的是最好的Thumbor http://thumbor.readthedocs.org/en/latest/index.html匹配,这是一个开源的,由一家大公司支持(意味着它不会在明天消失),并附带很多很好的功能,如检测什么是在裁剪时对图像很重要。

对于低成本加CDN,我建议将其与Cloudfront和AWS存储结合使用,或者与Cloudflare等免费CDN结合使用。这些可能不是性能最佳的CDN提供商,但至少仍然比一台服务器表现更好,并且还能以便宜的价格卸载您的图像服务器。此外,它将为您节省大量的带宽成本。

答案 6 :(得分:0)

如果每个不同的图像可以通过单个URL唯一识别,那么我只需使用像AKAMAI这样的CDN。让你的PHP脚本完成这项工作,让AKAMAI处理负载。

由于这类业务通常没有预算问题,因此这是我唯一能看到的地方。

修改:仅当您找到能够为您提供此类内容的CDN时才有效。

答案 7 :(得分:0)

现在,专用于此任务的图像调整大小服务正在解决这个完全相同的问题。它们提供以下功能:

  1. 在内置CDN中 - 您无需担心图像分发
  2. 动态调整图像大小 - 任何所需尺寸都可用
  3. 无需存储 - 您只需存储基本图像,所有变体均由服务处理
  4. 生态系统库 - 您可以只包含javascript,并且您的工作已经完成,适用于所有设备和所有浏览器。
  5. 其中一项服务是Gumlet。您还可以尝试一些开源替代方案,如nginx插件,它也可以动态调整图像大小。

    (我为Gumlet工作。)