24

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

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

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

有谁知道可以即时缩小图像的图像处理服务器,所以我们只需要提供原始图像,网络人员可以请求他们需要的任何大小?

要求:

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

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

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

提前感谢您的任何建议。

4

8 回答 8

29

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

它是这样工作的:

  • 您请求将所需大小附加到文件名的图像,例如http://imageserver/someimage.150x120.jpg
  • 如果图片存在则不做其他处理直接返回(这是重点,缓存检查是隐式的)
  • 如果图像不存在,则通过 .htaccess 处理 404 not found 并将请求重新路由到生成所需大小图像的脚本
  • 在脚本中指定允许大小的列表以避免攻击,如脚本请求每个可能的大小来关闭服务器
  • 将其保存在无 cookie 域上,以尽量减少不必要的流量

编辑:我不认为 PHP 本身会减慢这个过程,因为在这种情况下 PHP 脚本被减少到最低限度:图像缩放是由用 C 编写的内置库完成的。无论你做什么,你都必须使用像这样的库(GD 或 libmagick 左右)所以这是不可避免的。使用我的系统,至少您完全跳过了检查缓存的开销,从而进一步减少了 PHP 交互。您可以在现有服务器上实现此功能,因此我想这是一个非常适合您预算的解决方案。

于 2010-02-14T11:00:56.193 回答
7

基于

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

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

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

对于“即时调整大小”方面 - 带宽对您来说有多重要?我想假设您对电影进行了如此多的处理,以至于每个请求中增加几 kb 的图像不会造成太大的伤害。如果是这种情况,您可以使用更大的图像并设置宽度和高度,让浏览器为您进行缩放。

于 2010-02-10T17:29:35.787 回答
4

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

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

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

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

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

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

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

SO上的其他答案 如何最好地在服务器外调整图像大小

于 2010-02-13T05:03:19.990 回答
2

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

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

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

您将无法以任何其他方式提供数百万张调整大小的图像。这就是 Google 和 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;
}
于 2010-02-14T10:50:19.740 回答
1

您正在寻找的内容与 Thumbor http://thumbor.readthedocs.org/en/latest/index.html最匹配,它是开源的,由一家大公司提供支持(意味着它明天不会消失),并附带许多不错的功能,例如在裁剪时检测图像上的重要内容。

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

于 2016-02-16T07:38:23.190 回答
1

在提出这个问题的时候,一些公司已经涌现来处理这个确切的问题。这不是孤立于您或您的公司的问题。许多公司已经到了需要为其图像处理需求寻找更持久的解决方案的地步。

imgix这样的服务用作图像操作的代理和 CDN,例如调整大小和应用覆盖。通过操纵 URL,您可以对每个图像应用不同的转换。imgix 每天处理数十亿个请求。

您也可以自己建立服务并将它们放在 CDN 后面。像imageproxy这样的开源项目非常适合这一点。这会给您的运营团队带来维护负担。

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

于 2015-08-06T17:47:03.070 回答
0

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

由于这种业务通常不会出现预算问题,因此我会只考虑那里。

编辑:仅当您确实找到了可以为您提供此类内容的 CDN 时才有效。

于 2010-02-14T10:55:49.340 回答
0

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

  1. 内置 CDN - 您无需担心图像分发
  2. 动态调整图像大小 - 可提供所需的任何大小
  3. 无需存储 - 您只需存储基本映像,所有变体都由服务处理
  4. 生态系统库 - 您可以只包含 javascript,并且您的工作已针对所有设备和所有浏览器完成。

其中一项服务是Gumlet。您还可以尝试一些开源替代品,例如 nginx 插件,它也可以动态调整图像大小。

(我为 Gumlet 工作。)

于 2017-12-08T21:57:11.067 回答