Nginx实现对图片的自由裁切和压缩传输

1,302 阅读5分钟

背景

之前在使用阿里云OSS(对象存储)的服务时候一直比较好奇,只需要简单的在图片的访问链接上加上参数,就可以按照定义的功能,得到裁剪或者处理好的图片。并且访问的时候 还可以对图片自动压缩处理。

image.png

刚好最近看到了一些文章介绍 Nginx的 http_image_filter_module模块使用,果断尝试了之后,确实 get 了一项很实用的技能或者说是工具。

运行原理和优缺点分析

http_image_filter_module是Nginx提供的集成图片处理模块,支持nginx-0.7.54以后的版本,在网站访问量不是很高;磁盘有限不想生成多余的图片文件的前提下,就可以用它实时缩放图片,旋转图片,验证图片有效性以及获取图片宽高以及图片类型信息,由于是实时计算的结果,所以网站访问量极大的话,不建议使用。

Nginx图片处理http_image_filter_module 的优缺点:

优点

  • (1)操作简单。通过简单配置,省去了后端裁剪程序的复杂性;

  • (2)实时裁剪。可以实时访问在线裁剪图片;

  • (3)灵活性强。后端程序裁剪图片时需要知道裁剪图片的尺寸和质量,使用nginx裁剪可以实时裁剪任意尺寸的图片;

  • (4)不占用硬盘空间;

缺点

  • (1)消耗CPU和内存,访问量大的时候就会给服务器带来很大的负担。(可以通过使用Nginx缓存和缓存服务器来解决);

  • (2)功能不是很强大,支持的处理图片类型只包括JPG、JPEG、GIF、PNG or WebP;

开始实践起来吧!

使用docker启动一个自由裁切的nginx图片服务器

1. 首先创建相应的目录和配置文件

#启动之前需要把对应的目录创建好
mkdir -p /data/nginx/conf.d /data/nginx/logs /data/nginx/html

#创建的nginx的配置文件
touch /data/nginx/nginx.conf

2. 定义nginx的配置文件(本地图片资源)

user nginx;
worker_processes auto;
worker_rlimit_nofile 65535; #进程限制
error_log /var/log/error.log warn;
pid /run/nginx.pid;

load_module /etc/nginx/modules/ngx_http_image_filter_module.so; #加载相应的模块配置

include /usr/share/nginx/modules/*.conf;
events {
    worker_connections 8192;
    use epoll;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 4096;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/conf.d/*/*.conf;
    
    #配置对图片的压缩传输
    gzip on;
    gzip_comp_level 6;
    gzip_min_length 256;
    gzip_types application/javascript text/plain application/x-javascript text/javascript text/css application/xml image/svg+xml image/jpeg image/png;
     
    #定义配置文件
    server {
        listen       80;
        listen       [::]:80;
        server_name  _;
        
        location ~* /images/.*\.(jpg|gif|png)$ {
            root   /usr/share/nginx/html;

            # 图片默认宽度
            set $width -;

            # 图片默认高度
            set $height -;
            if ($arg_width != "") {
                set $width $arg_width;
            }
            if ($arg_height != "") {
               set $height $arg_height;
            }

            #image_filter off;
            #image_filter test;
            #image_filter size;
            #image_filter rotate 90;
            image_filter resize $width $height;
            #image_filter crop 300 200;
            image_filter_buffer 100M;
            image_filter_interlace on;
            image_filter_jpeg_quality 95;
            image_filter_sharpen 100;
            image_filter_transparency on;
        }
    }
}


3. 启动一个 my-nginx 容器,并且将图片资源挂载上去。

#这里我使用的是 nginx:1.25版本

docker run -it -p  80:80 --name my-nginx \
-v /data/nginx/nginx.conf:/etc/nginx/nginx.conf \
-v /data/nginx/conf.d:/etc/nginx/conf.d \
-v /data/nginx/logs:/var/log/nginx \
-v /data/nginx/html:/usr/share/nginx/html \
-d \
--restart=always \
nginx:1.25

4.传入图片 进行测试

#把准备好的图片拷贝到指定的目录中
cp mac-photo.jpg /data/nginx/html/images

5. 对比使用效果

通过访问地址 

http://test.domain/images/mac-photo.jpg?height=200 
http://test.domain/images/mac-photo.jpg?width=300 
http://test.domain/images/mac-photo.jpg?width=600&height=500

就可以访问到按照比例出现的图。注意这里面有个问题是:图像显示是按照比例呈现的。并非任意的比都可以实现。 并且也不会超过其最大的比例

这是原图

image.png

这是裁切到500像素的效果 访问 test.domain/images/mac-…

image.png

这是裁切到100像素的效果 访问 test.domain/images/mac-…

image.png

6.如果想对其他网站上的图片进行裁切可以看这里(外部图片资源)

如下是nginx.conf的配置文件 使用proxy_pass 做图片代理

user nginx;
worker_processes auto;
worker_rlimit_nofile 65535; #进程限制
error_log /var/log/error.log warn;
pid /run/nginx.pid;

load_module /etc/nginx/modules/ngx_http_image_filter_module.so;

include /usr/share/nginx/modules/*.conf;
events {
    worker_connections 8192;
    use epoll;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 4096;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/conf.d/*/*.conf;

    gzip on;
    gzip_comp_level 6;
    gzip_min_length 256;
    gzip_types application/javascript text/plain application/x-javascript text/javascript text/css application/xml image/svg+xml image/jpeg image/png;

    server {
        listen       80;
        listen       [::]:80;
        server_name  _;

        location ~* /.*\.(jpg|gif|png)$ {
        proxy_pass https://www.skyname.cn;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;

            # 图片默认宽度
            set $width -;

            # 图片默认高度
            set $height -;
            if ($arg_width != "") {
                set $width $arg_width;
            }
            if ($arg_height != "") {
               set $height $arg_height;
            }

            #image_filter off;
            #image_filter test;
            #image_filter size;
            #image_filter rotate 90;
            image_filter resize $width $height;
            #image_filter crop 300 200;
            image_filter_buffer 100M;
            image_filter_interlace on;
            image_filter_jpeg_quality 95;
            image_filter_sharpen 100;
            image_filter_transparency on;
        }
    }
}

这里 我们用 这张图片做示例www.skyname.cn/app/upload/…

image.png 访问test.domain/app/upload/… 可以看到图片被裁切变小

image.png

到这里就介绍完成了, 这里是一些参数详细解释。

image_filter off;
#关闭模块

image_filter test;
#确保图片是jpeg gif png否则返415错误

image_filter size;
#输出有关图像的json格式:如下显示{ "img" : { "width": 100, "height": 100, "type": "gif" } } 出错显示:{}

image_filter rotate 90|180|270;
#旋转指定度数的图像,参数可以包括变量,单独或一起与resize crop一起使用。

image_filter resize width height;
#按比例减少图像到指定大小,公减少一个可以另一个用"-"来表示,出错415,参数值可包含变量,可以与rotate一起使用,则两个一起生效。

image_filter crop width height;
#按比例减少图像比较大的侧面积和另一侧多余的载翦边缘,其它和rotate一样。没太理解

image_filter_buffer 10M;
#设置读取图像缓冲的最大大小,超过则415错误。

image_filter_interlace on;
#如果启用,最终的图像将被交错。对于JPEG,最终的图像将在“渐进式JPEG”格式。

image_filter_jpeg_quality 95;
#设置变换的JPEG图像的期望质量。可接受的值是从1到100的范围内。较小的值通常意味着既降低图像质量,减少传输数据,推荐的最大值为95。参数值可以包含变量。

image_filter_sharpen 100;
#增加了最终图像的清晰度。锐度百分比可以超过100。零值将禁用锐化。参数值可以包含变量。

image_filter_transparency on;
#定义是否应该透明转换的GIF图像或PNG图像与调色板中指定的颜色时,可以保留。透明度的损失将导致更好的图像质量。在PNG的Alpha通道总是保留透明度。