如何使用Docker使用imgproxy提供下一代图像

来自菜鸟教程
跳转至:导航、​搜索

作为 Write for DOnations 计划的一部分,作者选择了 Diversity in Tech Fund 来接受捐赠。

介绍

Web 开发人员最常用的 图像格式JPEGPNG.jpg 文件最适合用于照片,而 .png 文件最适合用于具有类似颜色的大块的图像,例如徽标或图表。 然而,这些格式分别于 1992 年和 1996 年首次开发。 近年来,视频压缩技术的进步导致了两种新的主要网络图像格式的发展:webpavif。 这些格式被称为“下一代”(或“下一代”)图像格式,可以免费使用,导致图像显着缩小(缩小 20% 到 50%),并得到 Web 浏览器的广泛支持。 由于 webpavif 会生成更小的图像,因此以这些新格式提供图像更高效、更快。

但是,使用“下一代”格式存在一些挑战。 首先,如果您希望在旧浏览器上为用户提供最大的兼容性,您可能需要根据需要使用 pngjpg。 其次,如果您有大量上传的图片库,则转换它们会很慢(而且成本很高)。

这是 图像代理 可以提供帮助的地方。 图像代理是一个应用程序,它将根据 Web 请求即时生成或检索自定义图像。 它们可用于裁剪或调整图像大小、添加水印或图像效果以及更改格式。 使用图像代理,您可以向最终用户提供看起来相同但效率更高的图像。

一种可用的代理称为 imgproxy。 它由 Evil Martians 提供支持,并且可以作为带有商业“Pro”扩展的自托管免费使用版本,如果您需要它们。 imgproxy 是用 Go 编写的,并使用 libvips 为其图像转换提供动力。 libvips 是可用的最快和最有效的图像处理库之一,非常适合根据用户的要求生成图像。

在本教程中,您将设置 imgproxy 并生成代理 URL 以修改图像。 您还将配置 imgproxy 以使用签名 URL 安全地提供图像,并在支持的情况下自动切换到“下一代”图像格式。

先决条件

要遵循本教程,您将需要:

  • 安装了 Docker 的服务器或开发机器。 对于 Ubuntu 20.04,您可以按照指南 如何在 Ubuntu 20.04 上安装和使用 Docker(或选择其他版本或发行版)。 对于 macOS 或 Windows,请参阅 Docker Desktop 的产品文档。
  • 用于运行示例脚本的脚本语言。 在本教程中,我们将使用 Ruby,您可以按照指南在 Ubuntu 上安装它,如何在 Ubuntu 20.04 上使用 rbenv 安装 Ruby on Rails。 在 macOS 上,您可以使用内置版本。
  • 熟悉浏览器开发工具会很有用。 对于 Chrome,请查看 官方文档 或教程 如何使用 Chrome 工具查找性能瓶颈

第 1 步 — 使用 Docker 安装 imgproxy

在此步骤中,您将安装 imgproxy 并检查它是否正常运行。 安装 imgproxy 有几种不同的方法,但在本教程中,您将使用 Docker,因为它是最便携的方法,并且在迁移到生产环境时意味着最小的变化.

在安装了 Docker 的服务器或开发机器上,运行以下命令来检查 Docker 是否正在运行:

docker info

输出的开头将类似于以下内容(如果您的容器或图像数量略有不同,请不要担心):

OutputClient:
 Context:    default
 Debug Mode: false
 Plugins:
  buildx: Docker Buildx (Docker Inc., v0.7.1)
  compose: Docker Compose (Docker Inc., v2.2.1)
  scan: Docker Scan (Docker Inc., v0.14.0)

Server:
 Containers: 15
  Running: 0
  Paused: 0
  Stopped: 15
 Images: 47
 Server Version: 20.10.11
 ...

确认 Docker 正在运行后,您可以使用 pull 子命令从 Dockerhub 下载最新版本的 imgproxy 映像:

docker pull darthsim/imgproxy:latest

在下载图像时,您将看到一组进度条。

接下来,您将使用 run 子命令启动容器实例:

docker run -p 8080:8080 -it darthsim/imgproxy

-p 选项告诉 Docker 将容器的端口 8080 映射到您机器上的端口 8080--it 选项告诉 Docker 你想要一个交互式终端(在这种情况下,你会想要查看 imgproxy 输出的日志)。 最后,darthsim/imgproxy 是你之前下载并想运行的镜像名称。

输出将类似于以下内容:

WARNING [2021-12-24T03:21:18Z] No keys defined, so signature checking is disabled
WARNING [2021-12-24T03:21:18Z] No salts defined, so signature checking is disabled
INFO    [2021-12-24T03:21:18Z] Starting server at :8080

不要担心警告; 您将在稍后的步骤中修复这些问题。

在此步骤中,您安装了 impgproxy 并确认它正在运行。 在下一步中,您将使用 imgproxy URL 请求修改后的图像。

第 2 步 — 创建 imgproxy URL 以修改图像

在此步骤中,您将创建可用于请求修改图像的 imgproxy URL。

使用 imgproxy 时,网站的最终用户向 imgproxy 发出 HTTP 请求。 然后,imgproxy 请求获取 source 图像。 源图像可以是我们的代理可以访问的 Internet 上的任何位置。 (您将在后面的步骤中确保这一点。)一旦获得源图像,imgproxy 根据您的要求修改图像,然后将该图像发送给最终用户。

您可以通过在 URL 参数中指定它们来告诉 imgproxy 您想要的修改类型。 imgproxy URL 使用以下格式:

http://your-imgproxy-host/%signature/%processing_options/%encoded_source_url.%extension

此 URL 中的 %signature 是可选的,您将在后面的步骤中配置它。 %processing_options 是您告诉 imgproxy 对图像进行裁剪或加水印之类的操作 - 或者如果您不想修改图像,可以完全忽略它。 %encoded_source_url 指向 source 图像,即您要求 imgproxy 修改的图像。 您需要对其进行编码,以便在您将其嵌入另一个 URL 时它仍然有效。 最后,可选的 %extension 参数允许您指定 imgproxy 使用通用文件扩展名转换为的图像文件类型,例如 .jpg

生成代理 URL 后,您可以将 img 标记的 src 属性替换为新的代理 URL。 例如,这是一个典型的图片 URL:

<img src="https://yourserver.com/assets/image.png" />

使用 imgproxy,您可以使用以下内容替换该 URL:

<img src="https://imgproxy.yourserver.com/your imgproxy url here />

当需要修改您的网站时,这将很有效。 但是,在学习使用 imgproxy 时,查看结果的最快方法是在您喜欢的 Web 浏览器的地址栏中输入 URL,它将立即呈现结果。 接下来,您将运行 imgproxy URL 的不同部分并使用浏览器访问它。

因为您在 Docker 中启动了 imgproxy,所以您的服务器在 localhost 上运行并侦听端口 8080。 这使您的 imgproxy 主机:

http://localhost:8080

注意: 如果您使用远程服务器而不是本地计算机来运行本教程,请将以下示例中的“localhost”替换为您的服务器的 IP 地址。 如果您的 URL 正确,您将在访问 http://your-server-ip:8080 时看到 嘿,我是 imgproxy 消息。


对于本教程,您可以使用在以下链接中找到的小狗图片:https://i.imgur.com/KSLD4VV.jpeg。 (此图像最初来自 Unsplash。)或者,您可以使用互联网上可访问的任何图像。 对于本教程,请确保使用照片(jpg 图像)而不是插图或徽标。

创建代理 URL 的第一步是对源 URL 进行编码。 这意味着您将 URL 中所有有意义的特殊字符(例如,:///.)替换为没有意义的安全字符。 这样,当您将源图像 URL 嵌入代理 URL 时,它仍然有效,并且客户端可以将其与代理 URL 分开。

imgproxy 使用 URL 安全的 Base64 编码。 您可能有一个最喜欢的编程语言可以用于此,或者在线 Base64 编码工具 可以解决问题(请务必选中标有“执行 URL 安全编码(使用 Base64URL 格式)”的框)。

注意: 您可以使用纯 URL 代替带有 /plain 选项的 Base64 编码,但这意味着您需要对任何查询参数进行编码。 一般来说,Base64 编码不易出错。


使用 URL-Safe Base64,样本小狗图片编码为 aHR0cHM6Ly9pLmltZ3VyLmNvbS9LU0xENFZWLmpwZWc。 您现在可以使用它来构建您的 imgproxy URL,并将其输入到您的网络浏览器中,如下所示:

http://localhost:8080/sig/aHR0cHM6Ly9pLmltZ3VyLmNvbS9LU0xENFZWLmpwZWc

注意: 如果您使用不同的图像进行测试,请将此 URL 的最后一部分替换为您的 Base64 编码的 URL。


加载后,您将看到未经修改的小狗图像。

注意: 如果看不到图像,则会显示错误消息,或者您可以检查步骤 1 中 docker run 命令的输出。 它将显示 imgproxy 日志,包括导致错误的任何请求。


接下来,您将使用 URL 的 .extension 部分更改图像格式。 在浏览器中打开以下内容:

http://localhost:8080/sig/aHR0cHM6Ly9pLmltZ3VyLmNvbS9LU0xENFZWLmpwZWc.png

这将需要更长的时间来加载,如果您查看浏览器开发者工具中的 Network 选项卡,您会发现此图像比原始图像大得多 - 在示例小狗图像的情况下, png 版本为 15.84MB,而原始 jpg 版本仅为 1.3MB。 当客户端请求图像时,imgproxy 自动将其从 jpg 修改为 png

您还可以将此图像转换为更有效的“下一代”格式,webpavif,方法是添加其中一种格式作为说明符,如下所示:

http://localhost:8080/sig/aHR0cHM6Ly9pLmltZ3VyLmNvbS9LU0xENFZWLmpwZWc.webp

测试图像的 webp 版本约为 600KB,与原始的 .jpg 相比增加了大约 53% d。 如您所见,使用这种图像格式可以加快对图像的请求。

您还可以更改图像大小或添加不同的效果。 为此,您可以将不同的修改指定为 %processing_options 参数。 在这种情况下,您将要求 imgproxy 使用 fillresizing_type 使用 size 将图像大小调整为 200 像素的宽度和 500 像素的高度(保持图像 纵横比 并在其之外裁剪部分图像),然后使用 5 的掩码添加 高斯模糊

通过在浏览器中访问以下 URL 来尝试一下:

http://localhost:8080/sig/size:200:500/resizing_type:fill/blur:5/aHR0cHM6Ly9pLmltZ3VyLmNvbS9LU0xENFZWLmpwZWc

如果您在浏览器中加载该 URL,您将看到测试图像的模糊和调整大小版本。

除了调整大小和模糊之外,您还可以使用许多不同的转换。 有关更多选项,请查看 imgproxy 文档的 生成 URL 部分

在此步骤中,您生成了 imgproxy URL 以提供和修改源图像。 接下来,您将保护 imgproxy 以便它只提供来自您使用 %signature URL 参数特别允许的 URL 的源图像。

第 3 步 — 使用签名保护 imgproxy

到目前为止,您一直在 imgproxy URL 的“签名”部分使用占位符。 但是,对您正在使用的 URL 进行 签名 很重要。 这意味着 imgproxy 将处理的唯一 URL 是您使用密钥/盐对创建的 URL。 这可以防止任何恶意使用您的服务器资源。 在此步骤中,您将配置 imgproxy 以要求签名并创建一个有效的签名以在您的 URL 中使用。

imgproxy 是一个 12-factor 应用程序,这意味着您可以使用环境变量对其进行配置,而无需修改任何文件。 这在 Docker 中运行时效果很好,因为 Docker 提供了 --env 标志来设置环境变量。

要创建签名,您首先需要一个密钥/盐对,它可以是任何随机的十六进制字符串。 您可以使用计算机上的随机数据或在线随机生成器工具生成它们。 确保指定 64 位数字,并生成至少两个字符串——一个用于键,一个用于盐。

生成两个 64 位的十六进制字符串,如下所示。

Example Random Hex49d5e2cd30d80fccc2e30877e4e58b2f0854a8dca6fb2e980b129171910080ed7ffa5dfbfde006e0c1a8ff52e7b5c614f0d3e9ec6e6ed754399fb0e2eb473c59

(请注意,您的字符串将与上述不同。)记录生成的字符串以在下一个命令中使用。

接下来,使用您在步骤 1 中使用的相同命令重新启动 Docker,但这次添加两个 --env 选项以将 IMGPROXY_KEYIMGPROXY_SALT 环境变量设置为随机生成的值正在运行的容器。 用您之前生成的字符串替换突出显示的部分。

docker run --env IMGPROXY_KEY=first-hex-result --env IMGPROXY_SALT=second-hex-result -p 8080:8080 -it darthsim/imgproxy

现在,如果您在 Web 浏览器中打开 http://localhost:8080/sig/aHR0cHM6Ly9pLmltZ3VyLmNvbS9LU0xENFZWLmpwZWc,您将收到“403 Forbidden”响应。 您还将在 Docker 日志中看到如下所示的条目:

Example Docker LogWARNING [2021-12-28T03:12:34Z] Completed in 134.6µs /sig/aHR0cHM6Ly9pLmltZ3VyLmNvbS9LU0xENFZWLmpwZWc  request_id=WQGTfRgeBXPvaQYHkNjab method=GET status=403 client_ip=172.17.0.1 error="Invalid signature"

正如您在该日志行的末尾所看到的那样,imgproxy 返回了 403 错误,因为您没有指定与 imgproxy 预期匹配的签名。 这意味着您已正确配置 imgproxy 以要求签名。 这个签名匹配是共享秘密认证的一个例子。 imgproxy 在内部使用请求的 URL 的 SHA256 哈希加上密钥和盐来计算签名。 为了生成相同的签名,必须提供相同的密钥和盐。 这意味着只有拥有您的 IMGPROXY_KEYIMGPROXY_SALT 值的人或程序才能生成您的 imgproxy 实例将响应的 URL。 没有这些值的用户无法使用您的资源。

注意:您不要直接在 URL 中指定键或盐值,因为这将被公开,无论是在网页源中还是在被其他浏览器或代理缓存时。 这就是为什么使用像 SHA256 这样的“单向”哈希很重要的原因——有人无法获取您的签名并使用它来重新创建您的秘密。


最后,为了发出有效的请求,您需要以与 imgproxy 相同的方式生成有效签名。 imgproxy Github 存储库中有许多 签名算法示例实现 ,但对于本教程,您将使用 Ruby 脚本。

使用 nano 或您喜欢的文本编辑器,创建一个名为 signature.rb 的文件:

nano signature.rb

将以下内容复制到您的文件中。 用您之前生成的 IMGPROXY_KEYIMGPROXY_SALT 值替换键盐对。 如果您使用的是远程服务器而不是本地计算机,您还应该将最后一行中的 localhost 输出替换为您的服务器 IP。

签名.rb

# adapted from https://github.com/imgproxy/imgproxy/blob/master/examples/signature.rb
require "openssl"
require "base64"

key = ["your IMGPROXY_KEY value"].pack("H*")
salt = ["your IMGPROXY_SALT value"].pack("H*")

url = "https://i.imgur.com/KSLD4VV.jpeg"

encoded_url = Base64.urlsafe_encode64(url).tr("=", "").scan(/.{1,16}/).join("/")

path = "/#{encoded_url}"

digest = OpenSSL::Digest.new("sha256")
hmac = Base64.urlsafe_encode64(OpenSSL::HMAC.digest(digest, key, "#{salt}#{path}")).tr("=", "")

signed_path = "/#{hmac}#{path}"

puts "Open http://localhost:8080#{signed_path} in a web browser"

首先,此代码块需要生成您的签名所需的库。 然后它使用 .pack("H*") 将密钥和盐字符串转换为十六进制字节,因为这是 OpenSSL 库所期望的(您可以 在此处 阅读有关打包和解包的更多信息)。

然后脚本 URL-safe Base64 对源 URL 进行编码,类似于您在步骤 1 中所做的。 虽然在这种情况下,它也会删除任何在 Base64 中用作填充的 = 字符,并用 / 分割每 16 个字符。 这些更改会生成一个好看的 URL。

一旦脚本具有编码的源 URL,它就会使用 SHA256 算法、密钥和盐生成签名。 然后签名本身会经过 Base64 编码,以便可以安全地包含在您的结果 URL 中。

最后,该脚本会打印出您可以在浏览器中打开的签名 URL。

运行此脚本以获取您的签名 URL。 您可以在终端中打开不同的选项卡,或按 Ctrl+C 停止 imgproxy,运行脚本,然后使用 docker 命令重新启动它。

ruby signature.rb

输出将类似于以下内容:

OutputOpen http://localhost:8080/-CAkkjs5IioquivOi5LYyDnVxEULmPK-xIwIwXTleUA/aHR0cHM6Ly9pLmlt/Z3VyLmNvbS9LU0xE/NFZWLmpwZWc in a web browser

URL 中突出显示的部分将有所不同。 这是您的签名 URL,只有您可以生成,因为只有您知道密钥/盐对。 如果您在浏览器中打开签名 URL,您将看到预期的图像而不是 403。 请务必保存您签名的 URL 以供后续步骤使用。

您现在已将 imgproxy 配置为需要签名 URL,并且您生成了有效的签名 URL。 现在只有有权访问您的密钥/盐对的人或程序才能与您的 imgproxy 对话。

接下来,您将配置 imgproxy 以自动提供最现代的图像格式。

第 4 步 — 配置 imgproxy 以自动提供“下一代”图像

到目前为止,您一直明确告诉 imgproxy 返回图像时使用哪种格式。 另一种选择是告诉 imgproxy 尽可能使用最有效的图像格式。 这意味着只有在用户浏览器支持的情况下,您才能提供“下一代”格式(webpavif)。 否则,imgproxy 将自动回退到 pngjpg

imgproxy 通过检查 Accept 标头来执行此操作,该标头在 Web 浏览器请求检查支持的图像格式时自动发送。 您可以在 MDN 上查看图像的默认 Accept 标头的 列表。 例如,Chrome 发送:

image/avif,image/webp,image/apng,image/*,*/*;q=0.8

因为同时存在 image/avifimage/webp(相应图像格式的 MIME 类型 ),我们知道 Chrome 支持这两种图像格式。

您可以使用 AVIF/WebP Support Detection 指示 imgproxy 使用最现代的支持图像格式。 要启用此功能,请将 IMGPROXY_ENABLE_WEBP_DETECTION 环境变量设置为 true,您可以通过在调用 docker run 时指定它来执行此操作。

首先,使用 Ctrl+C 停止正在运行的 Docker 映像。 然后,使用以下命令重新启动它,确保用您的键和盐值替换突出显示的部分:

docker run --env IMGPROXY_ENABLE_WEBP_DETECTION=true --env IMGPROXY_KEY=your-key-from-Step-3 --env IMGPROXY_SALT=your-Salt-from-Step 3 -p 8080:8080 -it darthsim/imgproxy

这与您在步骤 3 中使用的 docker 命令相同,带有一个额外的 --env 标志以启用 webp 检测。

要检查配置,您将在浏览器中打开图像。 访问您在第 3 步中生成的签名 URL,如下所示:

Example Image URLhttp://localhost:8080/-CAkkjs5IioquivOi5LYyDnVxEULmPK-xIwIwXTleUA/aHR0cHM6Ly9pLmlt/Z3VyLmNvbS9LU0xE/NFZWLmpwZWc

将为您提供 webp 图像。 您可以通过在浏览器开发人员工具中检查请求大小和类型或使用 curl 来验证这一点。 要使用 curl 进行检查,请在终端中打开一个新选项卡,以便 imgproxy 保持运行并使用以下命令,将突出显示的 URL 替换为您自己的:

curl -s -v -H "Accept: image/avif,image/webp,image/apng,image/*,*/*;q=0.8" http://localhost:8080/M-RTYvp5xktEK9gG93hPwAB6on9aX7H5XciGsI3XSac/aHR0cHM6Ly9pLmlt/Z3VyLmNvbS9LU0xE/NFZWLmpwZWc

-s 标志意味着使输出静音,因为当以纯文本输出时图像没有多大意义。 -v 表示详细,以便您可以看到请求和响应标头。 最后,-H 选项将 Accept 标头设置为与 Chrome 的相同。

输出将类似于以下内容:

Output*   Trying ::1:8080...
* Connected to localhost (::1) port 8080 (#0)
> GET /M-RTYvp5xktEK9gG93hPwAB6on9aX7H5XciGsI3XSac/aHR0cHM6Ly9pLmlt/Z3VyLmNvbS9LU0xE/NFZWLmpwZWc HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.77.0
> Accept: image/avif,image/webp,image/apng,image/*,*/*;q=0.8
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
< Cache-Control: max-age=3600, public
< Content-Disposition: inline; filename="KSLD4VV.webp"
< Content-Length: 601246
< Content-Type: image/webp
< Expires: Tue, 28 Dec 2021 09:08:32 GMT
< Server: imgproxy
< Vary: Accept
< X-Request-Id: I3sp-dXATs2AClfpHcSg-
< Date: Tue, 28 Dec 2021 08:08:32 GMT
<
* Failure writing output to destination
* Closing connection 0

您可以在 Content-Type 标头中看到 imgproxy 已返回 webp 图像。

如果您愿意,可以对 IMGPROXY_ENABLE_AVIF_DETECTION 环境变量执行相同的操作。 这同样适用,但适用于 avif 格式而不是 webp

在此步骤中,您将 imgproxy 配置为自动使用下一代图像格式,当且仅当它们受用户浏览器支持时。

结论

在本教程中,您了解了新的图像格式以及如何通过 Docker 运行、配置和保护 imgproxy。 您还将 imgproxy 配置为使用可以处理它们的浏览器自动将 webp 图像提供给任何网站访问者。 您现在已准备好将 imgproxy 部署到将自动运行 Docker 容器以动态转换图像并提高 Web Vitals 分数的任何地方。

下一步,您可以探索使用 imgproxy 处理图像的其他选项。 您可以在产品文档中查看 处理选项 的完整列表。 您还可以探索其他 配置选项 。 最后,您可以 在 Digital Ocean App Platform 上部署您的 imgproxy 容器。