溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

nginx+lua+GraphicsMagick生成實(shí)時(shí)縮略圖-CentOS7

發(fā)布時(shí)間:2020-08-05 14:05:22 來源:網(wǎng)絡(luò) 閱讀:4511 作者:botaozhao 欄目:建站服務(wù)器

背景

大多數(shù)的系統(tǒng)都會(huì)涉及縮略圖的處理,比如新聞系統(tǒng)和電商系統(tǒng),特別是電商系統(tǒng),每個(gè)商品大圖都會(huì)對(duì)應(yīng)一系列尺寸的縮略圖用于不同業(yè)務(wù)場(chǎng)景的使用。部分系統(tǒng)也會(huì)生成不同尺寸的縮略圖以供PC、手機(jī)端、ipad端使用。

解決方案探索:

  1. 直接加載原圖,使用css樣式表來控制圖片的寬高。顯然不太合適,大家也盡量不要這樣做。
  2. web程序在上傳成功后,同時(shí)生成相應(yīng)縮略圖。這種做法效率較低,如果遇到批量導(dǎo)入的業(yè)務(wù)時(shí)嚴(yán)重影響性能。有些圖片的縮略圖很少使用到,如果能按需生成豈不更好?
  3. 使用第三方提供的云存儲(chǔ)及數(shù)據(jù)處理服務(wù),解決圖片的處理、存儲(chǔ)、多節(jié)點(diǎn)訪問速度的問題,這種方式優(yōu)點(diǎn)是方案成熟,相應(yīng)的有一定費(fèi)用和開發(fā)工作,另外有一些小概率的風(fēng)險(xiǎn),比如云服務(wù)掛掉影響本站訪問。

本文使用的是Nginx+Lua+GraphicsMagick實(shí)現(xiàn)縮略圖功能,圖片的上傳及刪除還是交由web服務(wù)處理,縮略圖由單獨(dú)的模塊去完成。最終效果類似淘寶圖片,實(shí)現(xiàn)自定義圖片尺寸功能,可根據(jù)圖片加后綴100x100.jpg(固定高寬),-100.jpg(定高),_100-.jpg(定寬)形式實(shí)現(xiàn)自定義輸出圖片大小。

github源碼地址:https://github.com/botaozhao/nginx-lua-GraphicsMagick

更新說明

2018-2-9: 加入縮略圖尺寸限制,需在demo.conf中配置開關(guān)及允許的尺寸,代碼片段為:

init_by_lua '
    -- 開關(guān) 需要限制縮略圖尺寸:true ,不需要限制縮略圖尺寸:false
    image_sizes_check = true
    -- 允許的尺寸
    image_sizes = {"800x800", "400x400", "100x100", "-800", "-400", "-100", "800-", "400-", "100-"}
';

說明

文件夾規(guī)劃

img.xxx.com(如/usr/local/filebase)
├─upload
│  └─img
│    ├─001.jpg
│    └─002.jpg

自定義尺寸后的路徑

thumb(/tmp/thumb,可在conf文件里面更改)
├─upload
│  └─img
│    ├─001.jpg_100x100.jpg #固定高和寬
│    ├─001.jpg_-100.jpg #定高
│    ├─001.jpg_200-.jpg #定寬
│    └─002.jpg_300x300.jpg #固定高和寬
  • 其中img.xxx.com為圖片站點(diǎn)根目錄,img目錄是原圖目錄
  • 縮略圖目錄根據(jù)保持原有結(jié)構(gòu),并單獨(dú)設(shè)置目錄,可定時(shí)清理。

鏈接地址對(duì)應(yīng)關(guān)系
原圖訪問地址:http://img.xxx.com/upload/img/001.jpg
縮略圖訪問地址:http://img.xxx.com/upload/img/001.jpg_100x100.jpg 即為寬100,高100
自動(dòng)寬地址: http://img.xxx.com/upload/img/001.jpg_-100.jpg 用"-"表示自動(dòng),即為高100,寬自動(dòng)
自動(dòng)高地址: http://img.xxx.com/upload/img/001.jpg_200-.jpg 用"-"表示自動(dòng),即為寬200,高自動(dòng)

訪問流程

  • 首先判斷縮略圖是否存在,如存在則直接顯示縮略圖;
  • 縮略圖不存在,則判斷原圖是否存在,如原圖存在則拼接graphicsmagick(gm)命令,生成并顯示縮略圖,否則返回404

安裝

系統(tǒng)環(huán)境
centOS7 X64 虛擬機(jī)內(nèi)最小化安裝
以下操作均在此系統(tǒng)中操作,僅供參考
1、環(huán)境準(zhǔn)備

yum install -y wget  git 
yum install -y gcc gcc-c++ zlib zlib-devel openssl openssl-devel pcre pcre-devel
yum install -y libpng libjpeg libpng-devel libjpeg-devel ghostscript libtiff libtiff-devel freetype freetype-devel
yum install -y GraphicsMagick GraphicsMagick-devel

如果提示沒有GraphicsMagick的可用安裝包,請(qǐng)自行安裝GraphicsMagick,具體可參考我的另一篇文章:CentOS7下安裝GraphicsMagick1.3.21。
2、下載相關(guān)應(yīng)用

cd /usr/local/src
wget http://nginx.org/download/nginx-1.8.0.tar.gz
wget http://luajit.org/download/LuaJIT-2.0.4.tar.gz
wget http://zlib.net/fossils/zlib-1.2.8.tar.gz

3、下載nginx組件

git clone https://github.com/alibaba/nginx-http-concat.git
git clone https://github.com/simpl/ngx_devel_kit.git
git clone https://github.com/openresty/echo-nginx-module.git
git clone https://github.com/openresty/lua-nginx-module.git

解壓安裝

tar -zxf nginx-1.8.0.tar.gz
tar -zxf LuaJIT-2.0.4.tar.gz
tar -zxf zlib-1.2.8.tar.gz

1、安裝LuaJIT

cd LuaJIT-2.0.4
make -j8
make install 
export LUAJIT_LIB=/usr/local/lib
export LUAJIT_INC=/usr/local/include/luajit-2.0
ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2
cd ..

2、安裝nginx

cd nginx-1.8.0
./configure --prefix=/usr/local/nginx \
--sbin-path=/usr/local/nginx/sbin/nginx \
--conf-path=/usr/local/nginx/conf/nginx.conf \
--pid-path=/usr/local/nginx/pid/nginx.pid  \
--lock-path=/usr/local/nginx/pid/nginx.lock \
--error-log-path=/usr/local/nginx/logs/error.log \
--http-log-path=/usr/local/nginx/logs/access.log \
--with-http_ssl_module \
--with-http_realip_module \
--with-http_sub_module \
--with-http_flv_module \
--with-http_dav_module \
--with-http_gzip_static_module \
--with-http_stub_status_module \
--with-http_addition_module \
--with-http_spdy_module \
--with-pcre \
--with-zlib=../zlib-1.2.8 \
--add-module=../nginx-http-concat \
--add-module=../lua-nginx-module \
--add-module=../ngx_devel_kit 
make -j8
make install

編譯nginx常見問題

  1. ./configure: error: invalid option "–with-http_spdy_module"
    nginx 1.9.5 已經(jīng)沒有了 --with-http_spdy_module ,取代的是 --with-http_v2_module,若使用該版本或者更高版本請(qǐng)自行替換。
    官方說明文檔:http://nginx.org/en/docs/http/ngx_http_v2_module.html

配置

相關(guān)配置文件結(jié)構(gòu)位置

/usr/local/nginx
│  ├─conf
│      ├─......
│      └─nginx.conf
│  ├─html
│  ├─logs
│  ├─lua
│      ├─autoSize.lua
│      └─cropSize.lua
│  ├─pid
│  ├─sbin
│  └─vhost
│      └─demo.conf

相關(guān)的配置文件可以去我的github上去下載,地址為:nginx-lua-GraphicsMagick。
下面貼出詳細(xì)的配置文件內(nèi)容,和github上一致,可直接跳過。

修改nginx配置文件

cd /usr/local/nginx/
vi conf/nginx.conf
user root;
worker_processes 4;
worker_cpu_affinity 1000 0100 0010 0001;
error_log /usr/local/nginx/logs/error.log error;
pid /usr/local/nginx/pid/nginx.pid;
worker_rlimit_nofile 65535;
events
{
    use epoll;
    worker_connections 65535;
}
http
{
    limit_conn_zone $binary_remote_addr zone=one:10m;
    limit_conn_zone $server_name zone=perserver:10m;
    include mime.types;
    include fastcgi.conf;
    default_type application/octet-stream;
    charset utf-8;
    server_names_hash_bucket_size 128;
    client_header_buffer_size 32k;
    large_client_header_buffers 4 64k;
    sendfile on;
    autoindex off;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 120;

    fastcgi_connect_timeout 60;
    fastcgi_send_timeout 60;
    fastcgi_read_timeout 60;
    fastcgi_buffer_size 128k;
    fastcgi_buffers 8 128k;
    fastcgi_busy_buffers_size 128k;
    fastcgi_temp_file_write_size 128k;

    gzip on;
    gzip_min_length 1k;
    gzip_buffers 4 16k;
    gzip_http_version 1.0;
    gzip_comp_level 2;
    gzip_types text/plain application/x-javascript text/css application/xml;
    gzip_vary on;

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

    client_max_body_size 200m;

    #lua_package_path "/etc/nginx/lua/?.lua";

    include /usr/local/nginx/vhost/*.conf;
}

修改站點(diǎn)配置
普通站點(diǎn)的配置文件,包含固定高寬和定高,定寬兩種模式配置

cd /usr/local/nginx/
mkdir vhost 
vi vhost/demo.conf
#定義lua縮略圖支持的圖片尺寸及開關(guān)
init_by_lua '
    -- 開關(guān) 需要限制縮略圖尺寸:true ,不需要限制縮略圖尺寸:false
    image_sizes_check = true
    -- 允許的尺寸
    image_sizes = {"800x800", "400x400", "100x100", "-800", "-400", "-100", "800-", "400-", "100-"}
';

server {
    listen   80;
    index index.php index.html index.htm;

    set $root_path '/var/www';
    root $root_path;

    location /lua {
        default_type 'text/plain';
        content_by_lua 'ngx.say("hello, ttlsa lua")';
    }

    location / {
        try_files $uri $uri/ /index.php?$args;

        # add support for img which has query params,
        # like:  xxx.jpg?a=b&c=d_750x750.jpg
        if ($args ~* "^([^_]+)_(\d+)+x(\d+)+\.(jpg|jpeg|gif|png)$") {
            set $w $2;
            set $h $3;
            set $img_ext $4;

            # rewrite ^\?(.*)$ _${w}x${h}.$img_ext? last;
            rewrite ([^.]*).(jpg|jpeg|png|gif)$  $1.$2_${w}x${h}.$img_ext? permanent;
        }
    }

    # set var for thumb pic
    set $upload_path /usr/local/filebase;
    set $img_original_root  $upload_path;# original root;
    set $img_thumbnail_root $upload_path/cache/thumb;
    set $img_file $img_thumbnail_root$uri;

    # like:/xx/xx/xx.jpg_100-.jpg or /xx/xx/xx.jpg_-100.jpg
    location ~* ^(.+\.(jpg|jpeg|gif|png))_((\d+\-)|(\-\d+))\.(jpg|jpeg|gif|png)$ {
            root $img_thumbnail_root;    # root path for croped img
            set $img_size $3;

            if (!-f $img_file) {    # if file not exists
                    add_header X-Powered-By 'Nginx+Lua+GraphicsMagick By Botao';  #  header for test
                    add_header file-path $request_filename;    #  header for test
                    set $request_filepath $img_original_root$1;    # origin_img full path:/document_root/1.gif
                    set $img_size $3;    # img width or height size depends on uri
                    set $img_ext $2;    # file ext
                    content_by_lua_file /usr/local/nginx/lua/autoSize.lua;    # load lua
            }
    }

    # like: /xx/xx/xx.jpg_100x100.jpg
    location ~* ^(.+\.(jpg|jpeg|gif|png))_(\d+)+x(\d+)+\.(jpg|jpeg|gif|png)$ {
            root $img_thumbnail_root;    # root path for croped img

            if (!-f $img_file) {    # if file not exists
                    add_header X-Powered-By 'Nginx+Lua+GraphicsMagick By Botao';  #  header for test
                    add_header file-path $request_filename;    #  header for test
                    set $request_filepath $img_original_root$1;    # origin_img file path
                    set $img_width $3;    # img width
                    set $img_height $4;    # height
                    set $img_ext $5;    # file ext
                    content_by_lua_file /usr/local/nginx/lua/cropSize.lua;    # load lua
            }
    }

    # if need (all go there)
    location ~* /upload {
            root $img_original_root;
    }

    location ~ /\.ht {
        deny all;
    }
}

裁切圖片lua工具

cd /usr/local/nginx/
mkdir lua 

lua文件夾下需要兩個(gè)文件

  • autoSize.lua 定高或定寬模式裁切圖片處理lua腳本
  • cropSize.lua 固定高寬模式裁切圖片處理lua腳本
    autoSize.lua文件內(nèi)容為:

    -- 根據(jù)輸入長(zhǎng)或?qū)挼某叽缱詣?dòng)裁切圖片大小
    -- 檢測(cè)路徑是否目錄
    local function is_dir(sPath)
    if type(sPath) ~= "string" then return false end
    
    local response = os.execute("cd " .. sPath)
    if response == 0 then
        return true
    end
    return false
    end
    -- 文件是否存在
    function file_exists(name)
    local f = io.open(name, "r")
    if f ~= nil then io.close(f) return true else return false end
    end
    -- 獲取文件路徑
    function getFileDir(filename)
    return string.match(filename, "(.+)/[^/]*%.%w+$") --*nix system
    end
    -- 獲取文件名
    function strippath(filename)
    return string.match(filename, ".+/([^/]*%.%w+)$") -- *nix system
    end
    --去除擴(kuò)展名
    function stripextension(filename)
    local idx = filename:match(".+()%.%w+$")
    if (idx) then
        return filename:sub(1, idx - 1)
    else
        return filename
    end
    end
    --獲取擴(kuò)展名
    function getExtension(filename)
    return filename:match(".+%.(%w+)$")
    end
    function getImgSize(img)
    end
    -- 判斷尺寸是否合法
    -- check image size
    function table.contains(table, element)
    for _, value in pairs(table) do
      if value == element then
         return true
      end
    end
    return false
    end
    if image_sizes_check
    then
    if not table.contains(image_sizes, ngx.var.img_size)
    then
        ngx.exit(404);
    end
    end
    -- check image end
    -- 開始執(zhí)行
    -- ngx.log(ngx.ERR, getFileDir(ngx.var.img_file));
    local gm_path = 'gm'
    -- check image dir
    if not is_dir(getFileDir(ngx.var.img_file)) then
    os.execute("mkdir -p " .. getFileDir(ngx.var.img_file))
    end
    -- 獲取高寬 100!或!100模式
    local uri = ngx.var.img_size
    local width = string.sub(uri,1,1)
    local height = 0
    if width == "-" then
    width = 0
    height = string.sub(uri,2,string.len(uri))
    else
    width = string.sub(uri,1,string.len(uri)-1)
    height = 0
    end
    -- ngx.log(ngx.ERR,uri)
    -- ngx.log(ngx.ERR,width)
    -- ngx.log(ngx.ERR,height)
    -- ngx.log(ngx.ERR,ngx.var.img_file);
    -- ngx.log(ngx.ERR,ngx.var.request_filepath);
    -- 裁剪后保證等比縮圖 (缺點(diǎn):裁剪了圖片的一部分)
    -- 如: gm convert autoSize.jpg -resize x200 -quality 100 +profile "*" autoSize.jpg_-200.jpg
    if (file_exists(ngx.var.request_filepath)) then
    local cmd = gm_path .. ' convert ' .. ngx.var.request_filepath
    if height == 0 then
        cmd = cmd .. " -resize " .. width .. "x" ..  ""
    else
        cmd = cmd .. " -resize " .. "x" .. height .. ""
    end
    -- 由于壓縮后比較模糊,默認(rèn)圖片質(zhì)量為100,請(qǐng)根據(jù)自己情況修改quality
    cmd = cmd .. " -quality 100"
    cmd = cmd .. " +profile \"*\" " .. ngx.var.img_file;
    ngx.log(ngx.ERR, cmd);
    os.execute(cmd);
    ngx.exec(ngx.var.uri);
    else
    ngx.exit(ngx.HTTP_NOT_FOUND);
    end

    cropSize.lua文件內(nèi)容為:

    -- 根據(jù)輸入長(zhǎng)和寬的尺寸裁切圖片
    -- 檢測(cè)路徑是否目錄
    local function is_dir(sPath)
    if type(sPath) ~= "string" then return false end
    
    local response = os.execute("cd " .. sPath)
    if response == 0 then
        return true
    end
    return false
    end
    -- 文件是否存在
    function file_exists(name)
    local f = io.open(name, "r")
    if f ~= nil then io.close(f) return true else return false end
    end
    -- 獲取文件路徑
    function getFileDir(filename)
    return string.match(filename, "(.+)/[^/]*%.%w+$") --*nix system
    end
    -- 獲取文件名
    function strippath(filename)
    return string.match(filename, ".+/([^/]*%.%w+)$") -- *nix system
    end
    --去除擴(kuò)展名
    function stripextension(filename)
    local idx = filename:match(".+()%.%w+$")
    if (idx) then
        return filename:sub(1, idx - 1)
    else
        return filename
    end
    end
    --獲取擴(kuò)展名
    function getExtension(filename)
    return filename:match(".+%.(%w+)$")
    end
    -- 判斷尺寸是否合法
    -- 待切割的圖片尺寸
    local img_width_height = ngx.var.img_width .. "x" .. ngx.var.img_height;
    -- check image size
    function table.contains(table, element)
    for _, value in pairs(table) do
      if value == element then
         return true
      end
    end
    return false
    end
    if image_sizes_check
    then
    if not table.contains(image_sizes, img_width_height)
    then
        ngx.exit(404);
    end
    end
    -- check image end
    -- 開始執(zhí)行
    -- ngx.log(ngx.ERR, getFileDir(ngx.var.img_file));
    local gm_path = 'gm'
    -- check image dir
    if not is_dir(getFileDir(ngx.var.img_file)) then
    os.execute("mkdir -p " .. getFileDir(ngx.var.img_file))
    end
    --  ngx.log(ngx.ERR,ngx.var.img_file);
    --  ngx.log(ngx.ERR,ngx.var.request_filepath);
    -- 裁剪后保證等比縮圖 (缺點(diǎn):裁剪了圖片的一部分)
    -- gm convert cropSize.jpg -thumbnail 300x300^ -gravity center -extent 300x300 -quality 100 +profile "*" cropSize.jpg_300x300.jpg
    if (file_exists(ngx.var.request_filepath)) then
    local cmd = gm_path .. ' convert ' .. ngx.var.request_filepath
    cmd = cmd .. " -thumbnail " .. ngx.var.img_width .. "x" .. ngx.var.img_height .. "^"
    cmd = cmd .. " -gravity center -extent " .. ngx.var.img_width .. "x" .. ngx.var.img_height
    -- 由于壓縮后比較模糊,默認(rèn)圖片質(zhì)量為100,請(qǐng)根據(jù)自己情況修改quality
    cmd = cmd .. " -quality 100"
    cmd = cmd .. " +profile \"*\" " .. ngx.var.img_file;
    --  ngx.log(ngx.ERR, cmd);
    os.execute(cmd);
    ngx.exec(ngx.var.uri);
    else
    ngx.exit(ngx.HTTP_NOT_FOUND);
    end

    訪問

    開啟80端口

    firewall-cmd --permanent --zone=public --add-port=80/tcp
    firewall-cmd --reload

    啟動(dòng)nginx

    cd /usr/local/nginx/
    ./sbin/nginx 

    訪問查看圖片
    分別訪問下面幾個(gè)地址,測(cè)試能否查看及生成縮略圖
    http://XXX/upload/img/001.jpg
    http://XXX/upload/img/001.jpg_-200.jpg
    http://XXX/upload/img/001.jpg_200X200.jpg
    效果如下:
    nginx+lua+GraphicsMagick生成實(shí)時(shí)縮略圖-CentOS7
    此時(shí)服務(wù)器端也已經(jīng)在相應(yīng)路徑下生成了縮略圖文件:
    nginx+lua+GraphicsMagick生成實(shí)時(shí)縮略圖-CentOS7
    至此,nginx+lua+GraphicsMagick生成實(shí)時(shí)縮略圖完成!

    優(yōu)化

    現(xiàn)在已經(jīng)實(shí)現(xiàn)了服務(wù)器端實(shí)時(shí)生成縮略圖,為了避免服務(wù)器被被惡意改變寬和高參數(shù)而隨意生成大量文件,浪費(fèi)資源和空間,所以,我們加入了尺寸限制,具體配置已再demo.conf配置文件中標(biāo)出。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI