博客 | NGINX

NGINX 和 NGINX Plus 轻松提供响应式图像

NGINX-F5-horiz-black-type-RGB 的一部分
Liam Crilly 缩略图
利亚姆·克里利
2016 年 9 月 29 日发布
关于使用 NGINX Plus 图像过滤模块生成响应式图像,以实现更具响应性的网页设计的文章的杂志架图片。
照片: 菲尔·罗德

使用 Image‑Filter 模块和 srcset 标签动态调整图像大小

响应式网页设计已经成为现代网站和网络应用的标准,它可以在各种设备上提供一致的体验,同时优化每台设备的显示效果。 然而,现代设备不仅屏幕尺寸不同,而且像素密度也不同。 HTML5 img标签提供了许多功能,如果服务器提供多种变体,则这些功能可使浏览器选择最合适的资产。 如果网站部署了同一图像的多种不同尺寸,则网络浏览器可以选择最适合其当前环境的尺寸。

因此,响应式图像可以让 Web 浏览器产生与设计师意图最接近的渲染效果。 这提高了用户体验,但也给开发和运营团队带来了额外的负担,他们现在必须创建和部署大量图像资产变体以及默认图像。

在这篇博文中,我们展示了如何使用 NGINX 和 NGINX Plus 的图像过滤器模块来提供响应式图像,而无需创建和管理大量图像资产变体 - 相反,我们可以部署每个图像的单个“源”版本,NGINX 或 NGINX Plus 可以动态调整其大小。 文章中的信息适用于 NGINX 开源和 NGINX Plus(安装图像过滤器模块中的单独说明除外);为简洁起见,我们始终参考 NGINX Plus。

srcset属性

传递响应式图像的主要工具是 HTML5 img标签的srcset属性。 我们可以使用它来为不同像素密度和视口大小指定多个图像资产变体。 视口是 Web 浏览器可用显示空间的通用术语,无论是桌面上的窗口还是移动设备上的全屏应用程序。

在以下示例中, src属性为不支持srcset属性的浏览器定义了一个默认图像,并且srcset属性命名了两个变体 - 一种用于标准像素密度( 1x )的显示器,另一种用于双倍像素密度的显示器,例如 Apple Retina™ 显示器和一些 4K 显示器( 2x )。

< img src= "/images/mylogo-default.png" srcset= "/images/mylogo-density1.png 1x, /images/mylogo-density2.png 2x" >

以下更为复杂的示例根据视口的宽度定义了要显示的多个图像资产变体。 size属性指定,如果视口宽度大于10em ,则浏览器在视口的一半内呈现图像,否则使用整个视口。 浏览器确定图像可用的空间大小,并选择最适合可用空间的图像资产变体,通常“向上舍入”到下一个宽度( w后缀)并在内部调整图像大小以精确填充空间。

< img src= "/images/racecar-default.jpg" size= "(最小宽度: 10em)50vw,100vw“ srcset = ”/images/racecar-100px.jpg 100w,/images/racecar-225px.jpg 225w,/images/racecar-450px.jpg 450w,/images/racecar-675px.jpg 675w“ >

通过指定一系列图像资产变体来提供响应式图像的方法易于编码并且非常有效。 然而,在创建和管理图像变体本身方面,它带来了挑战。 您必须进行大量的预制作图像大小调整,并在服务器上部署大量文件。 微调每个变体的最佳数量和大小可能非常耗时,从而导致难以测试每个图像资产变体是否可访问。

有关srcset属性和其他响应式图像技术的更多信息,请参阅这篇精彩的博客文章

安装图像过滤器模块

NGINX Plus 和 NGINX 获取图像过滤器模块的过程不同。

为 NGINX Plus 安装图像过滤模块

对于 NGINX Plus 用户来说,Image‑Filter 模块是一个免费的动态模块

  1. 通过从 NGINX Plus 存储库安装来获取模块本身。

    对于 Ubuntu 和 Debian 系统:

    $ sudo apt-get install nginx-plus-module-image-filter

    对于 RedHat、CentOS 和 Oracle Linux 系统:

    $ sudo yum install nginx-plus-module-image-filter
  2. 通过在nginx.conf配置文件的顶层(“主”)上下文中(即不在http上下文中)包含load_module指令来启用该模块。

    load_module模块/ngx_http_image_filter_module.so;
  3. 重新加载 NGINX Plus,将 Image‑Filter 模块加载到正在运行的实例中。

    $ sudo nginx -s 重新加载>

安装 NGINX 开源图像过滤模块

安装 Image‑Filter 模块的最简单方法是从官方 NGINX 存储库获取它。 按照这些说明配置您的系统以访问官方 NGINX 存储库,然后使用您的操作系统包管理器进行安装。

对于 Ubuntu 和 Debian 系统:

$ sudo apt-get install nginx-module-image-filter

对于 Red Hat、CentOS 和 Oracle Linux 系统:

$ sudo yum install nginx-module-image-filter

安装后,按照安装 NGINX Plus 的图像过滤模块中的步骤 2 和 3 配置 NGINX 并重新加载。

还可以从源代码编译图像过滤器模块并将其作为静态编译或动态模块加载。 有关详细信息,请参阅NGINX Plus 管理指南

将图像大小与像素密度匹配

使用 Image‑Filter 模块,我们可以创建和部署每个图像的单个“源”版本,并让 NGINX Plus 动态调整其大小以提供浏览器请求的任何尺寸变体。 我们可以完全在 HTML 源内微调响应式网页和图像,而无需手动调整大小并将图像部署到我们的 Web 服务器。

在此示例 HTML 文件中,我们针对具有不同像素密度的设备定义了四种图像变体。

< html > < head > < title >响应式徽标 title >  head > < body > < h2 >根据像素密度选择徽标 h2 > < img src= "/img400/mylogo.png" srcset= "/img400/mylogo.png 1x, /img800/mylogo.png 2x, /img1200/mylogo.png 3x, /img1600/mylogo.png 4x" >  body >  html >

/img400/img800/img1200/img1600目录实际上并不存在。 相反,以下 NGINX Plus 配置将匹配以/img为前缀的资产请求,并将其转换为调整原始文件名中图像大小的请求(例如,前面的 HTML 中的mylogo.png )。

服务器 { listen 80;
root /var/www/public_html;

location ~ ^/img([0-9]+)(?:/(.*))?$ {
alias /var/www/source_images/$2;
image_filter_buffer 10M;
image_filter resize $1 -;
}
}

服务器块定义 NGINX Plus 如何处理传入的 HTTP 请求。 listen指令告诉 NGINX Plus 监听端口 80 - HTTP 流量的默认端口。 root指令指定了该网站在磁盘上的位置。 在这个简单的示例中,我们使用由 NGINX Plus 托管的静态网站,但 NGINX Plus 也常常充当动态内容的反向代理或 FastCGI 等应用连接器。所有这些用例都可以通过将源图像部署到 NGINX Plus 服务器来利用此处所述的 Image‑Filter 模块。

位置块使用正则表达式来匹配存储在以/img开头且后跟一个或多个数字的任何目录中的资产的请求。 数字被捕获为变量$1并且后面的文件名被捕获为变量$2 。 然后,我们使用别名指令从包含源图像的磁盘目录中满足此请求。 注意,该目录不在路径下,因此客户端无法直接请求源图像。

由于我们的源图像可能非常大,可能有数千像素宽,我们需要确保图像过滤器模块分配足够的内存来加载和调整它们的大小。 在这个例子中,我们使用image_filter_buffer指令来支持最大 10 MB 的图像文件。

最后, image_filter指令告诉 Image‑Filter 模块将源图像的大小调整为从/img目录名后缀捕获的宽度。 破折号(-)告诉 NGINX Plus 保持源图像的纵横比。

生产使用注意事项

将图像大小与像素密度匹配中描述的配置可以根据用于请求图像的目录名称提供图像的任意尺寸变体。 但是,对于生产环境,我们不想等待 Web 服务器根据每个请求调整图像大小。 这对于总体延迟来说并不好,并且还会增加大量的 CPU 开销。

最有效的解决方案是缓存我们调整大小后的图像变体,以便每个变体的后续请求都从缓存中提供,而无需经过图像过滤器模块。 我们可以通过 NGINX Plus 配置实现这一点,即定义一个单独的虚拟服务器来执行图像大小调整,并且仅当请求的图像大小尚未在缓存中时才将请求代理到该服务器。 我们称之为响应式图像服务器

NGINX Plus 的生产配置,其中包含用于前端 Web 服务器和响应式图像服务器的“位置”块。 为了实现响应更快的网页设计,网页服务器会使用图像过滤器模块缓存图像服务器创建的尺寸变体。
图 1. 在前端 Web 服务器上启用缓存的响应式图像服务器的生产配置

我们还必须考虑允许任意请求执行图像调整大小操作所带来的安全隐患。 如果攻击者对唯一的图像资产变体(例如/img1001/mylogo.png/img1002/mylogo.png/img1003/mylogo.png等)发出快速请求,则配置缓存也无济于事。 即使请求量相对较少,此类攻击也可能因 CPU 利用率过高而导致拒绝服务 (DoS)。 为了解决这个问题,我们对响应式图像服务器应用了速率限制,但不对包含缓存变体的前端服务器应用速率限制。 以下配置通过对图像过滤器模块应用缓存和速率限制来扩展将图像大小与像素密度匹配中的配置。

proxy_cache_path /var/www/imgcache levels=1 keys_zone=resized:1m max_size=256m;
服务器 {
listen 80;
root /var/www/public_html;

location ~ ^/img([0-9]+)(?:/(.*))?$ {
proxy_pass http://127.0.0.1:9001;
proxy_cache resized;
proxy_cache_valid 180m;
}
}

limit_req_zone "1" zone=2persec:32k rate=2r/s;

服务器 {
listen 9001;
allow 127.0.0.1;
denied all;
limit_req zone=2persec burst=10;

位置 ~ ^/img([0-9]+)(?:/(.*))?$ {
别名 /var/www/source_images/$2;
image_filter_buffer 10M;
image_filter 调整大小 $1 -;
}
}

我们首先使用proxy_cache_path指令定义缓存图像的位置。 keys_zone参数为缓存索引(称为resized )定义了一个共享内存区域并分配了 1 MB,足以容纳大约 8,000 个调整大小的图像。 max_size参数定义 NGINX Plus 开始从缓存中删除最近最少请求的图像以便为新的缓存项目腾出空间的点。

前端 Web 服务器(监听端口 80)的location指令使用proxy_pass指令将以/img为前缀的请求发送到内部托管的响应式图像服务器(127.0.0.1:9001)。 proxy_cache指令通过指定用于存储响应图像服务器的响应的缓存名称( resized )来启用此位置的缓存。 proxy_cache_valid指令确保调整大小后的图像在缓存中保存至少 180 分钟(除非缓存已超过max_size并且它们是最近最少请求的图像之一),并且不会缓存响应图像服务器的任何错误响应。

有关缓存的深入描述,请参阅NGINX 和 NGINX Plus 缓存指南

在定义响应式图像服务器本身之前,我们使用limit_req_zone指令指定速率限制。 该指令本身并不强制执行速率限制 - 它定义了每秒两个请求的速率限制,然后通过在其服务器块中包含limit_req指令将其应用于响应图像服务器(参见下一段)。 通常,速率限制是根据请求的属性来定义的,但在这种情况下,我们指定静态键值“1” ,以便限制适用于所有请求者。 我们将共享内存区域的大小设置为最小可能值 3 KB,因为我们的密钥具有固定基数 1。

响应式图像服务器的服务器块监听端口 9001。 我们包含允许拒绝指令来指定只有本地主机(前端 Web 服务器)可以连接到响应图像服务器。 然后,我们通过包含limit_req指令来应用先前定义的速率限制; burst参数在强制执行速率限制之前允许 10 个并发请求。 一旦速率限制生效,超额请求将被延迟,直到可以在限制范围内处理为止。

位置块与匹配图像大小和像素密度中的块相同,但现在仅当请求的图像不在缓存中且未超过速率限制时才执行。

在这个基本配置中,单个 NGINX Plus 实例既充当前端 Web 服务器,又充当响应式图像服务器。 图像处理的计算量非常大,可能会导致极高的工作负载并使 NGINX Plus 容易受到 DoS 攻击。 为了避免由于所有工作进程都忙于处理图像调整大小请求而导致前端 Web 服务器无法立即接受新请求的情况,我们建议运行一个专用于图像处理的单独 NGINX Plus 实例。 这将前端 Web 服务器的工作进程与执行图像处理的进程隔离开来。 要在同一主机上运行单独的 NGINX Plus 实例,请在命令行上指定不同的配置文件:

$ sudo nginx -c /etc/nginx/resize-server.conf

响应式图像的实际应用

查看响应式图像实际效果的最有效方法是观察浏览器如何随着视口大小的变化决定使用哪个srcset图像变体。 这是一个简单图像库的 HTML 源代码。 请注意,出于演示目的,每个图像的尺寸变体略有不同,从而创建了许多可能的“断点”,浏览器可以在这些断点处选择不同的变体。

<!DOCTYPE html>
<html>
<>
<标题>响应式图片库</标题>
</>
<身体>
 
<氢键>响应式图片库</氢键>

<图片 源=“/img100/1-多米诺骨牌.jpg” 尺寸=“(最小宽度: 20em)40vw,100vw“ srcset = ”/img110/1-dominos.jpg 110w,/img210/1-dominos.jpg 210w,/img310/1-dominos.jpg 310w,/img410/1-dominos.jpg 410w,/img510/1-dominos.jpg 510w,/img610/1-dominos.jpg 610w“ > < img src = “/img100/2-sign.jpg” sizes = “(最小宽度: 20em)40vw,100vw“ srcset = ”/img120/2-sign.jpg 120w,/img220/2-sign.jpg 220w,/img330/2-sign.jpg 330w,/img420/2-sign.jpg 420w,/img520/2-sign.jpg 520w,/img620/2-sign.jpg 620w“ > < img src = “/img100/3-thruppence.jpg” sizes = “(最小宽度: 20em) 40vw, 100vw” srcset= “/img130/3-thruppence.jpg 130w, /img230/3-thruppence.jpg 230w, /img330/3-thruppence.jpg 330w, /img440/3-thruppence.jpg 440w, /img550/3-thruppence.jpg 550w, /img660/3-thruppence.jpg 660w” > < img src= “/img100/4-aces.jpg” sizes= “(最小宽度: 20em)40vw,100vw” srcset = “/img140/4-aces.jpg 140w,/img240/4-aces.jpg 240w,/img340/4-aces.jpg 340w,/img440/4-aces.jpg 440w,/img540/4-aces.jpg 540w,/img640/4-aces.jpg 640w” >  body >  html >

下面的截图显示了 Chrome 浏览器中此网页的内容,其中检查器打开了“网络”选项卡。 名称列显示从服务器请求的每个图像变体的路径,这使我们能够看到所选的大小,而无需检查 Web 服务器上的日志。

在图 2 中的窄视口中,浏览器选择了宽度在 220 到 310 像素之间的图像(名称列中/img目录名称上的数字后缀介于这些值之间)。

图 2. 当视口较窄时响应式图库

当我们在图 3 中扩大浏览器窗口时,浏览器会选择宽度在 440 到 540 像素之间的图像(列出的最后四张图像)。 Initiator列中这些图像的值为Other

图 3. 当视口较宽时响应式图库

结论

借助 NGINX Plus 和 Image‑Filter 模块,我们可以根据当前浏览器条件提供最佳图像尺寸。 而且,我们无需进行预生产图像大小调整、批处理或管理磁盘上数百种图像资产变体即可做到这一点。 这只是 NGINX Plus 帮助您实现完美应用交付的另一种方式。

亲自尝试使用 NGINX Plus 实现响应式图像 - 立即开始30 天免费试用联系我们讨论您的用例


“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”