一、需求

根据用户的请求参数对图片动态裁剪并自动加上水印,将裁剪后的图片和原图保存在同一级目录

例如原图地址的路径为:/images/uploads/2021/09/01.jpg

按200x200裁剪后的图片路径为:/images/uploads/2021/09/01_200x200.jpg

当用户第一次请求200x200的图片时,会自动将图片动态裁剪为200x200的大小,并自动加上水印。

二、实现方式

1、nginx自带的http_image_filter_module模块

 image filter module使用的是GD,性能、效率、处理后的图片质量不如 GraphicsMagick,并且裁剪后也不会保存,这样每次请求过来都要重新裁剪,会导致访问速度很慢。

image filter module 不会真正生成裁剪/缩放后的图片,而是通过 Nginx 直接输出的,这样每次请求或缓存过期后都需要重新裁剪/缩放,这样无疑会增加 Nginx 的负担。

2、搭配Lua+GraphicsMagick实现

使用Lua+GraphicsMagick裁剪的图片会保存在磁盘上,用户以后再访问就不再做裁剪,而是直接取之前保存的裁剪后的图片,从而访问效率相比前者好很多。

三、FastDFS集群模块架构

FastDFS 系统有三个角色:跟踪服务器(Tracker Server)、存储服务器(Storage Server)和客户端(Client)。

  • Tracker Server: 跟踪服务器,主要做调度工作,起到均衡的作用;负责管理所有的storage server和group,每个storage在启动后会连接 Tracker,告知自己所属 group 等信息,并保持周期性心跳。多个Tracker之间是对等关系,不存在单点故障。
  • Storage Server: 存储服务器,主要提供容量和备份服务;以 group 为单位,每个 group 内可以有多台 storage server,组内的storage server上的数据互为备份。
  • Client: 客户端,上传下载数据的服务器。

四、系统环境

节点

IP地址

操作系统

安装软件

Tracker Server

192.168.5.106

Centos 7.6 64位

Fastdfs

Tracker Server

192.168.5.154

Centos 7.6 64位

Fastdfs

Tracker Server

192.168.5.177

Centos 7.6 64位

Fastdfs

Storage Server

192.168.5.115

Centos 7.6 64位

Fastdfs+Nginx+Lua+GraphicsMagick

五、安装部署

全局操作(所有机器执行)

wget https://github.com/happyfish100/libfastcommon/archive/V1.0.38.tar.gz
wget https://github.com/happyfish100/fastdfs/archive/V5.11.tar.gz
mkdir /data/fastdfs

安装libfastcommon

tar zxvf V1.0.38.tar.gz
cd libfastcommon-1.0.38/
./make.sh
./make.sh install

安装fastdfs

tar zxvf V5.11.tar.gz
cd fastdfs-5.11/
./make.sh
./make.sh install

tracker节点操作

cp /etc/fdfs/client.conf.sample /etc/fdfs/client.conf
cp /etc/fdfs/tracker.conf.sample /etc/fdfs/tracker.conf

storage节点操作

cp /etc/fdfs/storage.conf.sample /etc/fdfs/storage.conf
cp /root/fastdfs-5.11/conf/http.conf /etc/fdfs/
cp /root/fastdfs-5.11/conf/mime.types /etc/fdfs/

修改tracker server配置

port=22122
base_path=/data/fastdfs

启动tracker server

/usr/bin/fdfs_trackerd /etc/fdfs/tracker.conf start
firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.5.0/24" port port="22122" protocol="tcp" accept'
firewall-cmd --reload

修改storage server配置

port=23000
base_path=/data/fastdfs
store_path0=/data/fastdfs
tracker_server=192.168.5.106:22122
tracker_server=192.168.5.154:22122
tracker_server=192.168.5.177:22122
http.server_port=8088

启动storage server

firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.5.0/24" port port="23000" protocol="tcp" accept'
firewall-cmd --reload
/usr/bin/fdfs_storaged /etc/fdfs/storage.conf start
fdfs_monitor /etc/fdfs/storage.conf list # 查看集群状态

修改Client配置

base_path=/data/fastdfs
tracker_server=192.168.5.106:22122
tracker_server=192.168.5.154:22122
tracker_server=192.168.5.177:22122

下载相关插件

wget https://github.com/happyfish100/fastdfs-nginx-module/archive/V1.20.tar.gz
tar zxvf V1.20.tar.gz
cd /opt/fastdfs-nginx-module-1.20/src/
cp mod_fastdfs.conf /etc/fdfs/
cd /opt/
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
wget https://github.com/openresty/lua-nginx-module/archive/v0.10.14.tar.gz
tar zxvf v0.10.14.tar.gz
wget http://luajit.org/download/LuaJIT-2.0.4.tar.gz
wget http://www.lua.org/ftp/lua-5.3.1.tar.gz
wget ftp://ftp.graphicsmagick.org/pub/GraphicsMagick/1.3/GraphicsMagick-1.3.18.tar.gz
yum -y install readline-devel

安装LuaJIT

tar -zxf LuaJIT-2.0.4.tar.gz
cd LuaJIT-2.0.4/
make && 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

安装lua

tar -zxvpf lua-5.3.1.tar.gz
cd lua-5.3.1/
make linux
make install

安装GraphicsMagick

tar zxvf GraphicsMagick-1.3.18.tar.gz
cd GraphicsMagick-1.3.18/
./configure --prefix=/usr/local/GraphicsMagick --enable-shared
make && make install

编译nginx

cd nginx-1.16.1/
./configure --prefix=/data/nginx --with-http_realip_module --with-http_sub_module --with-http_flv_module --with-http_dav_module --with-http_addition_module --with-http_stub_status_module --with-openssl=/usr/local/openssl --add-module=/opt/ngx_cache_purge-2.3 --with-http_gzip_static_module --with-http_ssl_module --with-stream --with-stream_ssl_module --add-module=/opt/nginx_upstream_check_module-master --add-module=/opt/fastdfs-nginx-module-1.20/src/ --with-http_image_filter_module --with-pcre --add-module=/opt/nginx-http-concat --add-module=/opt/lua-nginx-module-0.10.14 --add-module=/opt/ngx_devel_kit --add-module=/opt/echo-nginx-module --with-ld-opt=-Wl,-rpath,$LUAJIT_LIB
make
cp objs/nginx /data/nginx/sbin/

配置lua脚本

mkdir /data/nginx/conf/lua
cd /data/nginx/conf/lua/
chmod +x fastdfs.lua

fastdfs.lua内容


-- 写入文件
local function writefile(filename, info)
local wfile=io.open(filename, "w") --写入文件(w覆盖)
assert(wfile) --打开时验证是否出错
wfile:write(info) --写入传入的内容
wfile:close() --调用结束后记得关闭
end

-- 检测路径是否目录
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

-- 检测文件是否存在
local file_exists = function(name)
local f=io.open(name,"r")
if f~=nil then io.close(f) return true else return false end
end

-- 反向查找路径
function last_find(str, k)
local ts = string.reverse(str);
local _, i = string.find(ts, k);
return string.len(ts) - i + 1;
end

local area = nil
local originalUri = ngx.var.uri;
local originalFile = ngx.var.file;
local index = last_find(ngx.var.uri, "([0-9]+)x([0-9]+)");
if index then
originalUri = string.sub(ngx.var.uri, 0, index-2);
area = string.sub(ngx.var.uri, index);
index = string.find(area, "([.])");
area = string.sub(area, 0, index-1);

local index = last_find(originalFile, "([0-9]+)x([0-9]+)");
originalFile = string.sub(originalFile, 0, index-2)
end

-- check original file
if not file_exists(originalFile) then
local fileid = string.sub(originalUri, 2);
-- main
local fastdfs = require('restyfastdfs')
local fdfs = fastdfs:new()
fdfs:set_tracker("0.0.0.0", 22122)
fdfs:set_timeout(1000)
fdfs:set_tracker_keepalive(0, 100)
fdfs:set_storage_keepalive(0, 100)
local data = fdfs:do_download(fileid)
if data then
-- check image dir
if not is_dir(ngx.var.image_dir) then
os.execute("mkdir -p " .. ngx.var.image_dir)
end
writefile(originalFile, data)
end
end

-- 创建缩略图
local image_sizes = {"800x800","710x300","735x250","250x150","186x150","122x122","120x120","345x345","295x295","292x292","262x262","274x274","190x190","150x150","144x144","110x110","690x340","72x72","100x100","180x180","480x240","750x740","216x216","490x190","126x126"};
function table.contains(table, element)
for _, value in pairs(table) do
if value == element then
return true
end
end
return false
end

if table.contains(image_sizes, area) then
local bg;
if string.lower(string.sub(ngx.var.file,-3))=="jpg" then
bg=" -background white ";
else
bg=" -background transparent ";
end;

local command1 = "/usr/local/GraphicsMagick/bin/gm convert -quality 90 " .. originalFile .. " -thumbnail " .. area .. bg .. " -gravity center -extent " .. area .. " " .. ngx.var.file;
local command2 ="";
if area == "800x800" then
command2 = "/usr/local/GraphicsMagick/bin/gm composite -geometry +100+100 -dissolve 50 /data/nginx/image/yaotu1.png " .. ngx.var.file .. " " ..ngx.var.file;
else
command2 = "/usr/local/GraphicsMagick/bin/gm composite -geometry +60+60 -dissolve 50 /data/nginx/image/yaotu2.png " .. ngx.var.file .. " " ..ngx.var.file;
end;
os.execute(command1);
os.execute(command2);
end;

if file_exists(ngx.var.file) then
--ngx.req.set_uri(ngx.var.uri, true);
ngx.exec(ngx.var.uri)
else
ngx.exit(404)
end

上传水印图片

mkdir /data/nginx/image/

搭建FastDFS分布式集群通过Nginx+Lua+GraphicsMagick实现动态压缩图片_fastdfs

添加nginx配置

    server {
listen 8088;
server_name localhost;

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

location ~/group[0-9]/M00 {
#root /data/fastdfs;
alias /data/fastdfs/data;

set $image_root "/data/fastdfs/data";
if ($uri ~ "/([a-zA-Z0-9]+)/([a-zA-Z0-9]+)/([a-zA-Z0-9]+)/([a-zA-Z0-9]+)/(.*)") {
set $image_dir "$image_root/$3/$4/";
set $image_name "$5";
set $file "$image_dir$image_name";
}

if (!-f $file) {
content_by_lua_file "/data/nginx/conf/lua/fastdfs.lua";
}
ngx_fastdfs_module;
}

access_log /data/nginx/logs/img_access.log main;
}

六、测试

上传图片

fdfs_upload_file /etc/fdfs/client.conf ./c.jpg

上传成功后图片路径为

group1/M00/00/00/wKgFc2FEJuKAX9rKAAMGDjihSg4931.jpg​

访问图片

​http://xxx.xxx.xxx.xxx:8088/group1/M00/00/00/wKgFc2FEJuKAX9rKAAMGDjihSg4931.jpg​​

访问缩略图

http://xxx.xxx.xxx.xxx:8088/group1/M00/00/00/wKgFc2FEJuKAX9rKAAMGDjihSg4931.jpg_735x250.jpg​​​