如何优化用户上传的图片以及 Meteor 服务器上的现有图片

How to optimize images uploaded by the user and also existing images on the server in Meteor

目前,我的团队运行着一个 meteor 应用程序,其中包含数十万张全尺寸非常大的图像。我们早就应该这样做了,但我们需要一种方法来优化它们以帮助缩短加载时间。我正在寻找一种解决方案,当用户从我们的应用程序上传图像时(例如:全尺寸、中型、缩略图)能够保存多种尺寸的图像,并且还可以自动旋转并允许用户旋转。我们使用 Amazon S3 来托管我们所有的图像。我们还需要一种方法将所有现有图像从服务器端转换为这些尺寸格式。

不久前我尝试实施一些东西但没有成功。我在我们的服务器上设置了 imagemagick,但在生产中遇到了问题,因为图像被临时保存在服务器内存中进行处理,但由于内存量有限,这导致了崩溃。我对这种事情没有什么经验。

我的第二个想法是使用 HTML canvas 调整图像大小。我认为这适用于新上传的图像。但我仍在寻找一种方法来处理现有图像。

我考虑过:

如果有人能给我一些建议,让我开始行动,那将非常有帮助!

libvips 可以在不使用内存或磁盘的情况下调整图像大小 --- 像素以小块的形式流过系统,同时进行解码和重新编码。

例如,对于一张 10k x 10k 像素的 JPG 图片,我看到:

$ vipsheader wtc.jpg 
wtc.jpg: 9372x9372 uchar, 3 bands, srgb, jpegload
$ /usr/bin/time -f %M:%e vipsthumbnail wtc.jpg -s 5000x5000 -o x.jpg
98720:0.65

这是4核8线程的i7。它使用 98MB 内存并需要 0.65 秒的实时时间。有一个 chapter in the docs introducing vipsthumbnail.

为了与 ImageMagick 6 进行比较,我看到:

$ /usr/bin/time -f %M:%e convert wtc.jpg -resize 5000x5000 x.jpg
1263232:2.02

1.3GB 内存,需要 2 秒的实时时间——大约多 10 倍内存,慢 3 倍。

因为 vipsthumbnail 使用的内存很少,您可以将它与 GNU parallel 结合使用,而不需要服务器有很多很多 GB 的内存。在这个 i7 上,我可以同时 运行 四个并获得大约 4 倍的加速,因此可能比 ImageMagick 整体快 12 倍。

sharp is a popular node binding for libvips,这样可能更方便。还有 Python、Ruby、PHP、Go、Lua 等的绑定。

(免责声明:我是 libvips 维护者之一,所以我不是很中立)

我使用 https://www.imagemagick.org/ 调整图像大小、裁剪和旋转图像。它适用于流星。这将是探索的良好起点。 https://github.com/CollectionFS/Meteor-CollectionFS

我看到两种完全不同的预算方式:

  1. 这不是我推荐的方法,除非你只有 1-2 GB (https://transloadit.com/demos/file-importing/resize-all-images-in-an-s3-bucket/)

  2. Link 您的 S3 到 Cloudinary 服务并使用 Cloudinary 进行转换(您不会喜欢它($$)因为您拥有的图像数量)。

  3. 在AWS中,希望您使用Cloudfront来为您的资产提供服务。不管是什么改造技术你主要会做两件事:

    • 创建 1 个 Lambda 函数用于转换 S3 中所有新创建的资产。我所做的是 "monitor" 一个 S3 存储桶和所有进来的新东西触发我的 Lambda 函数,我在另外 2 个文件夹中创建资产,我最终得到:全分辨率、半分辨率和拇指分辨率。然后在 Meteor 中 link 每种尺寸都可以满足您的需要。最典型的情况是当您有一张用户个人资料图片时,您需要在聊天中将其显示为完整 header、列表或小拇指。
    • 创建 1 个 Lambda 边缘(我相信稍微多一点 $$)并附加到您的 Cloudfront 边缘以响应所有调用。如果当前存储量的存储成本对您来说不是太高,您可以在请求时转换图像并替换旧的较大图像,而不是 运行 它作为 1 次过程批量处理。

而不是 Lambda Edge,您可以设置一台带有 Node 的 EC2 机器和 运行 一个循环遍历所有 S3 资产并进行转换的函数。

反正我感觉你想做的都是AWS,和你的Meteor无关。还有一件事要做:在上传之前优化图像。如果您将 React 与 Meteor 结合使用,我可以为您提供必要的组件,否则我可以为您提供组件,然后您编写 Blaze 视图层或您可能使用的任何其他内容。

我有基于 ImageMagic 的生产中的 Lambda 转换函数,以防您有兴趣采用这种方式。我还计划 "upgrade" 这个函数使用 Sharp(就像在例子中那样)但是目前它在生产中做得很好,当我有时间的时候会切换。 检查这个例子:

  Download the image from S3, transform, and upload to a different S3 bucket or folder.

  const dstKeyResizedHalf = `p-half/` + imageName
  s3.getObject({
    Bucket: srcBucket,
    Key: srcKey
  }).promise()
    .then(data => Sharp(data.Body)
      .jpeg({
        chromaSubsampling: '4:4:4',
        progressive: true
      })
      .resize(WEB_WIDTH_MAX)
      .toFormat('jpg')
      .toBuffer()
    )
    .then(buffer => s3.putObject({
      Body: buffer,
      Bucket: dstBucket,
      ContentType: 'image/jpg',
      Key: dstKeyResizedHalf,
      CacheControl: 'max-age=864000'
    }).promise())
    .catch(err => callback(err))
}