来,动手搭个简单的图片编辑器!

一 前言

网页中有很多图片,我们经常需要对图片进行一些基本的加工和处理,常见操作的有裁剪、伸缩、旋转、加水印、格式转换等。

本文将使用开源的技术,主要使用 ImageMagick 和 OpenResty,通过编写少量的 Lua 代码和 Nginx 配置文件修改,来实现一个简单的图片处理服务,ImageMagick 处理图片会略耗资源,因此我们对已经处理过的图片用Redis做缓存,相同的URL请求再次过来时,目标图片会从 Redis 里获取并返回。

二 环境准备

本文所用的环境为滴滴云 Centos 7.4 版本,在搭建之前可以先准备一台云服务器。

三 开源软件安装

3.1 安装 OpenResty

进 OpenResty 的官网 openresty.org/cn/downlo 选择一个相对稳定的版本下载,我选择的版本是1.13.6.1。

1) 创建下载目录

mkdir -p /root/zhangjie/tools

2)下载

cd /root/zhangjie/tools

wget https://openresty.org/download/openresty-1.13.6.1.tar.gz

3)解压

tar zxvf openresty-1.13.6.1.tar.gz

4)安装依赖的包

yum -y install pcre-devel openssl-devel gcc gcc-c++ curl

5)编译和安装

mkdir -p /opt/app/openresty

cd openresty-1.13.6.1 

./configure --prefix=/opt/app/openresty --with-luajit
make -j2
make install

6) 确认安装成功

a) 启动和检查进程

cd /opt/app/openresty/nginx

sbin/nginx

ps -ef|grep nginx
root 11950 1  0 16:54 ?00:00:00 nginx: master process sbin/nginx
nobody   11951 11950  0 16:54 ?00:00:00 nginx: worker process
root 11955  2510  0 16:54 pts/000:00:00 grep --color=auto nginx

b) Windows下外网IP浏览

在浏览器里打开网址:

http://116.85.17.20

显示如下图所示的页面,即表示安装成功

v2-713829e63b74d1cfb8f62b7094707535_b.png


c) 检查Lua环境是否装成功

增加配置:

vi conf/nginx.conf

user nobody;

worker_processes 2;

…

http {
    ...
    server {

        ...

        location = /hello_lua {
            add_header Content-Type text/plain;
            content_by_lua '
            ngx.say("Hello, Lua!")  
            ';  
            }

        ...
    }

...
}

热加载配置:

sbin/nginx -s reload

在浏览器里打开网址:

http://116.85.17.20/hello_lua

显示如下图所示的页面,即表示 Lua 支持成功了


v2-4902c08613d0245f43e70eb6cc335bfc_b.png

3.2 安装 ImageMagick

1) 安装 ImageMagick

yum -y install ImageMagick ImageMagick-devel

2)确认安装成功

mkdir /opt/app/openresty/nginx/test

cd /opt/app/openresty/nginx/test

wget https://dicloud.didistatic.com/static/dicloudpub/webapp/img/login-banner.54c23bb.png

mv login-banner.54c23bb.png didiyun.png

convert -resize 100x100 didiyun.png didiyun_dest.png

最后一行 “convert” 命令执行成功,即表示安装成功。

3.3 获取 Lua 的 Magick 库

1)下载 Lua 的 Magick 库

选用开源的 leafo/magick, github 地址:github.com/leafo/magick

cd /root/zhangjie/tools

git clone https://github.com/leafo/magick.git

2) 确认库功能正常

cd /root/zhangjie/tools/magick

cp /opt/app/openresty/nginx/test/didiyun.png .

编写测试用的 Lua 程序 image_convert_test.lua:

[root@10-255-0-25 magick]# cat image_convert_test.lua 

local magick = require("magick")
magick.thumb("didiyun.png", "100x100", "didiyun_dest.png")

执行命令

/opt/app/openresty/luajit/bin/luajit image_convert_test.lua 

无报错并且有 “didiyun_dest.png” 文件生成,则证明库功能正常。

注意:

这里必须用 “/opt/app/openresty/luajit/bin” 目录下的检测 luajit,因为后面步骤中 Nginx 用的是此路径下的 luajit。

3)拷贝程序到 OpenResty Lua 库目录下

cp -r /root/zhangjie/tools/magick/magick /opt/app/openresty/lualib    

这样 OpenResty 查找依赖的 Lua 程序会找到。

3.4 安装 Redis

1)安装 Redis

yum -y install redis

2) 修改 Redis 配置文件

安装完后,如果要修改 Redis 配置文件,可以编辑 redis.conf,redis.conf:

/etc/redis.conf

这里用默认的配置就行了,不需要修改。

3) 启动 Redis Server

systemctl start  redis.service

4) 启动 Redis Client

[root@10-255-0-25 dc2-user]# redis-cli
127.0.0.1:6379> 
127.0.0.1:6379> keys *
(empty list or set)
127.0.0.1:6379> 
127.0.0.1:6379> set mykey didiyun
OK
127.0.0.1:6379> get mykey
"didiyun"
127.0.0.1:6379> 

出现以上输出,即表示 Redis 安装成功了。

5)设置 Redis 为开机自动启动

执行:

systemctl enable redis.service 


四 编写代码

4.1 准备工作

1)修改 Nginx 日志等级

为了方便 Lua 调试输出看日志,修改 Nginx 中的日志等级,在 “nginx.conf” 中,去掉以下行配置前的注释符 “#”,去掉后为:

error_log  logs/error.log  info;

2)创建图片源数据目录

图片源数据目录:

mkdir -p /opt/app/openresty/nginx/image

拷贝图片:

cp /opt/app/openresty/nginx/test/didiyun.png /opt/app/openresty/nginx/image

3)创建 Lua 逻辑代码目录

Lua 处理逻辑代码目录:

mkdir -p /opt/app/openresty/nginx/image_processor

4)增加 Nginx 图片后缀识别及 Lua 程序调用配置

在 nginx.conf 的 server 域增加配置(以 png 和 jpg 两种格式为例):

    location ~ .*\.(png|jpg)?$ {
        add_header Content-Type text/plain;
        content_by_lua_file image_processor/image_convert.lua;
    }

4.2 编码

1)image_convert.lua 编写

实现了图片的裁剪,拉伸缩放和旋转三种功能。

local redis = require "resty.redis"
local magick = require("magick")

local redis_client = redis:new()
local args = ngx.req.get_uri_args()
local image_service = args['s']

ngx.log(ngx.INFO, "uri:", ngx.var.uri)
ngx.log(ngx.INFO, "image service:", image_service)

redis_client:set_timeout(1000)
local ok,err = redis_client:connect('127.0.0.1',6379)
if not ok then
    ngx.log(ngx.ERR, "connect redis failed!")
    ngx.say("connect redis failed!")
    ngx.exit(0)
end

local source = '/opt/app/openresty/nginx/image'..ngx.var.uri
ngx.log(ngx.INFO, "source:", source)
if image_service == nil then
    -- 获取原图
    local file = io.open(source); 
    local source_image = file:read("*all")
    file:close()
    ngx.log(ngx.INFO, "get source image!")
    ngx.say(source_image)

elseif image_service == "thumb" then
    -- thumb 裁切(缩放)
    local img = assert(magick.load_image(source))

    -- args
    local image_action = args['a']

    -- get from cache
    local cache_key = image_service..ngx.var.uri..image_action
    local res, err = redis_client:get(cache_key)
    if getmetatable(res) ~= nil then
        -- cache hit
        ngx.log(ngx.INFO, image_service, ", cache hit! cache key: ", cache_key)
        ngx.say(res)
        ngx.exit(0)
    end

    -- cache miss
    local output = nil 
    ngx.log(ngx.INFO, image_service, ", source: ", source, ", args, image action: ", image_action)
    -- ngx.say("start thumb...")
    imageblob = magick.thumb(source, image_action, output)

    -- set to cache
    redis_client:set(cache_key, imageblob)
    if not ok then
        ngx.log(ngx.ERR, "cache set failed! err:", err)
    else
        ngx.log(ngx.INFO, "cache set OK!")
    end

    -- response
    ngx.say(imageblob)

elseif image_service == "resize" then
    -- 图片拉伸
    -- args, 目标宽和高
    local width = tonumber(args['w'])
    local height = tonumber(args['h'])

    -- source info
    local img = assert(magick.load_image(source))
    ngx.log(ngx.INFO, "source width:", img:get_width(), "source height:", img:get_height());
    ngx.log(ngx.INFO, image_service, ", arg, width:", width, ", height:", height)
    -- ngx.say("start resize...")

    img:resize(width, height)
    ngx.say(img:get_blob())

elseif image_service == "rotate" then
    -- 图片旋转
    -- args, 旋转度数
    local degrees = tonumber(args['d'])

    local img = assert(magick.load_image(source))
    img:rotate(degrees)
    ngx.say(img:get_blob())

else
    ngx.say("unknow image service!")
end

注意:

a) 代码仅为演示版,并未考虑参数注入等安全性问题,真实环境需要检查URI和Query String传入每个参数合法性及一些异常处理;

b)例子中仅做了”thumb”处理图片的缓存,”resize”和”rotate”缓存开发思路一样;

c)为了代码简洁Redis并未采用连接池的方式;

d)leafo/magick库中还有很多功能,可以添加更多代码,支持更丰富的图片处理功能。

2)检查配置和重启 Nginx

检查 Nginx 配置文件,输出 “syntax is ok” 表示配置文件正确:

[root@10-255-0-25 nginx]# /opt/app/openresty/nginx/sbin/nginx -t

nginx: the configuration file /opt/app/openresty/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /opt/app/openresty/nginx/conf/nginx.conf test is successful

重启 Nginx:

sbin/nginx -s reload


4.3 Url 中 Query 参数说明

v2-b2ae3fb6f7ad34bfe6d1c952b70d9b94_b.png

五 例子

5.1 测试并使用

在浏览器先后打开网址:

http://116.85.17.20/didiyun.png   
http://116.85.17.20/didiyun.png?s=thumb&a=64x64!
http://116.85.17.20/didiyun.png?s=thumb&a=500x300%2B50%2B120
http://116.85.17.20/didiyun.png?s=resize&w=800&h=800
http://116.85.17.20/didiyun.png?s=rotate&d=90

即能看到原图、裁切、缩放和旋转的效果。

注意:

若页面出现报错,可以看 Nginx 的 error.log 日志排查和解决。

六 参考资料

参考网址:

imagemagick.org

github.com/leafo/magick

openresty.org/cn

本文作者:张杰


文章看完啦,不如来官网看看?滴滴云3-5折活动聚惠来袭!

与其看教程,不如实际操作下?

滴滴云-为开发者而生

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
本设计将要实现的是一个单机图片浏览软件,从其目标功能及特点分析,将本软件功能划分如下: 可以打开BMP、PCX、TGA、JPEG及GIF图像。 不论打开的是上述图像中的哪一种,用户均能以其中的任意一种格式将其保存。 在查看一幅图像时,可以逐渐放大、缩小;可以直接调整图像显示大小到适合屏幕或恢复为原始状态;可以直接将显示大小调整为原图像的50%、75%、150%、200%。 在查看一幅图像时,可以对其进行顺时针90º、逆时针90º、180º旋转。 在查看一幅图像时,可以直接调用Windows画图程序打开并进行编辑或以系统默认关联的程序打开查看或编辑。 软件提供“上一张”、“下一张”的功能,即在打开一幅图像后,可以不再使用“打开”命令而用鼠标单击“上一张”按钮、“下一张”按钮或按键盘上的Page Up键、Page Down键直接浏览当前图片所在文件夹中的其他图片。 可以进行全屏幕浏览,并在全屏幕浏览时提供“幻灯片播放”的功能,自动显示当前文件夹下的所有图像。同时,在全屏幕浏览时,在屏幕右上角显示一个浮动工具条,提供“停止幻灯片播放”、“上一张”、“下一张”、“逐渐放大”、“逐渐缩小”、“适合屏幕大小”、“原始大小”及“退出全屏浏览”的功能。 按F11键可以进行全屏浏览、非全屏浏览的切换,同时,在进行全屏幕浏览时按ESC键也可以退出全屏状态。 在载入图片时,提供“从上往下”、“从下往上”、“从左往右”、“从右往左”、“左上进入”、“左下进入”、“右上进入”、“右下进入”、“马赛克”、“百叶窗”等显示效果,并且可以由用户选择是否使用及使用哪个效果,用户也可以选择让系统随机选择效果。 在查看图片时,用户也可以让软件随时显示“水平百叶窗”、“垂直百叶窗”、“马赛克”、“向上扫描”、“向下扫描”等效果。 在查看图片时,可以选择从当前目录中删除该图片,并将其放入系统回收站中。 在窗口的用户区右键单击鼠标,则弹出快捷菜单,显示常用的操作命令。 在查看图片时,标题栏显示当前打开的图片的文件名;状态栏从左到右依次显示图片的全路径、当前的显示比例、图片文件的大小(KB)、图像的大小、鼠标当前的坐标。
Pixie Image Editor 在线图片编辑器 中文版 功能 集成 - 轻松将pixie集成到任何现有项目或应用程序中。 可扩展 - Pixie接口和API可以使用新功能进行扩展。 移动 - Pixie拥有完整的移动支持,并可自动调整其界面以适应任何设备的大小。 可自定义的UI - 通过显示,隐藏或添加新菜单项,更改工具栏位置或使用不同的主题来自定义UI。 可翻译 - Pixie的界面可通过配置完全翻译。 水印 - 保存的照片可以使用指定的文本轻松加水印。 模式 - 在叠加(模态),内联或全屏模式之间进行选择。 工具API - 通过API使用所有精灵工具(调整大小,裁剪,框架等),而无需打开精灵界面。 可自定义的工具 - 所有工具都可完全自定义,您可以删除或修改和添加自定义贴纸,形状,字体,框架等。 状态 - 以json格式保存当前编辑器状态,允许使用预构建模板等功能。 照片处理 - 通过界面或API调整大小,裁剪,转换等。 滤镜 - Pixie配有许多内置滤镜,如灰度,模糊,黑白,复古等。可以通过API添加更多过滤器.. 框架 - 为任何大小的照片添加内置响应帧或添加自己的帧。 裁剪 - 将照片裁剪为指定宽高比之一,或让用户通过UI选择自定义裁剪区域。 绘图 - 功能强大的免费绘图工具支持鼠标和触摸,具有多种画笔类型,颜色等。 文本 - 完全支持向图像添加文本。可以使用数百种谷歌字体或仅使用自定义添加的字体。 形状 - 只需指定svg图像路径,即可轻松添加自定义形状。 贴纸 - 可以添加或删除自定义贴纸。任何类型的图像都可以用作贴纸。 角落 - 只需单击一下或API调用即可对图像角进行四舍五入。 空画布 - Pixie不必编辑现有照片,也可以从头开始轻松创建自定义图像。 历史记录 - 所有编辑器操作都是非破坏性的,可以通过历史记录工具轻松撤消和重做。 对象 - 所有对象(如贴纸,形状和文本)都在自己的图层上,可以通过更改颜色,添加阴影,背景等来轻松移动,调整大小,删除和修改。 图案和渐变 - 所有对象都可以使用许多内置或自定义图案和渐变填充。 保存 - 修改后的图像可以通过API或接口轻松保存在本地设备或服务器上。 缩放和平移 - 可以使用鼠标,鼠标滚轮或移动设备上的触摸和捏合手势来缩放和平移画布。 HTML5 - Pixie使用原生HTML5,这意味着它可以在每个设备上运行。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值