使用 OpenResty 构建高效的动态图片水印代理服务20250127
使用 OpenResty 构建高效的动态图片水印代理服务
在当今数字化的时代,图片在各种业务场景中广泛应用。为了保护版权、统一品牌形象,动态图片水印功能显得尤为重要。然而,直接在后端服务中集成水印功能,往往会带来代码复杂度增加、兼容性问题等诸多挑战。为了解决这些问题,我们可以利用 OpenResty 和 Lua 构建一套独立于后端应用的动态水印代理服务,既能大幅降低后端负担,又能增强系统的灵活性。
一、需求与解决方案
1.1 需求分析
在实际业务中,我们面临着多方面的需求,主要可以分为功能性需求、性能需求和扩展性需求:
功能性需求
- 按用户和图片 ID 获取图片并添加水印:根据用户提供的用户和图片 ID,获取对应的图片,并为其动态添加水印。
- 从指定 URL 加载图片并添加水印:支持用户输入指定的图片 URL,系统从该 URL 动态加载图片,并为其添加水印。
性能需求
- 高效处理图片:在高并发的情况下,能够快速、高效地处理图片,确保系统的响应速度。
- 避免重复处理:对于相同的图片,避免进行重复的处理,提高系统的性能和资源利用率。
扩展性需求
- 兼容多种图片格式:支持常见的图片格式,如 JPG、PNG、WEBP 等,以满足不同用户的需求。
- 方便添加其他处理功能:系统应具有良好的扩展性,能够方便地添加其他图片处理功能,如裁剪、缩放等。
1.2 解决方案
为了实现上述需求,我们采用以下方案:
- 使用 OpenResty 提供图片代理服务:OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台,通过 Lua 模块可以方便地实现图片水印功能。
- 解耦图片处理逻辑:将图片处理逻辑从后端服务中分离出来,通过代理实现独立的图片处理流程,避免对后端代码的修改。
- 利用 lua - resty - magick 与 Nginx 缓存机制:lua - resty - magick 是一个用于处理图片的 Lua 模块,能够高效地添加水印。同时,使用 Nginx 的缓存机制可以优化系统性能,减少重复处理。
二、实现步骤
2.1 安装与配置 OpenResty
以下是在 CentOS 系统上安装 OpenResty 和相关依赖的详细步骤:
Step 1: 更新系统并安装基础工具
sudo yum update -y
sudo yum groupinstall -y "Development Tools"
sudo yum install -y readline-devel pcre-devel openssl-devel tar gcc make tree perl curl
这些命令的作用是更新系统软件包,并安装编译和运行 OpenResty 所需的基础工具。
Step 2: 添加 OpenResty 官方 YUM 仓库并安装 OpenResty
wget https://openresty.org/package/centos/openresty.repo
sudo mv openresty.repo /etc/yum.repos.d/
sudo yum check-update
sudo yum install -y openresty
通过这些命令,我们添加了 OpenResty 的官方 YUM 仓库,并安装了 OpenResty。
Step 3: 安装 ImageMagick 和 LuaRocks
sudo yum install -y epel-release
sudo yum install -y ImageMagick ImageMagick-devel
sudo yum install -y luarocks
luarocks install magick
这里安装了 ImageMagick 及其开发库,用于图片处理。同时,安装了 LuaRocks 并使用它安装了 magick 模块。
验证安装
- 确认 OpenResty 正常运行:
sudo /usr/local/openresty/nginx/sbin/nginx
curl -I http://localhost
启动 OpenResty 的 Nginx 服务,并使用 curl
命令检查服务是否正常响应。
- 验证 magick 模块:
luajit -e "local magick = require('magick'); print('Magick module loaded successfully')"
运行此命令,如果输出 Magick module loaded successfully
,则说明 magick 模块安装成功。
2.2 图片代理服务实现
Lua 图片处理模块
以下是核心的图片处理逻辑,封装在 image_handler.lua
文件中:
local _M = {}
local magick = require("magick")
local tmp_dir = "/tmp/"function _M.process_image_with_watermark(image_data, ext)local input_path = tmp_dir .. "input_image." .. extlocal output_path = tmp_dir .. "output_image." .. extlocal watermark_path = "/var/www/images/watermark.png"-- 保存图片到临时路径local input_file = io.open(input_path, "wb")if not input_file thenngx.log(ngx.ERR, "Failed to open file for writing: ", input_path)ngx.status = ngx.HTTP_INTERNAL_SERVER_ERRORngx.say("Failed to process image")returnendinput_file:write(image_data)input_file:close()-- 添加水印逻辑local success, err = pcall(function()local img = magick.load_image(input_path)local watermark = magick.load_image(watermark_path)watermark:resize(img:get_width(), img:get_height())img:composite(watermark, 0, 0, "OverCompositeOp")img:write(output_path)img:destroy()watermark:destroy()end)if not success thenngx.log(ngx.ERR, "Image processing failed: ", err)ngx.status = ngx.HTTP_INTERNAL_SERVER_ERRORngx.say("Failed to process image")returnend-- 返回处理后的图片local output_file = io.open(output_path, "rb")if not output_file thenngx.log(ngx.ERR, "Failed to open processed image")ngx.status = ngx.HTTP_INTERNAL_SERVER_ERRORngx.say("Failed to process image")returnendlocal output_data = output_file:read("*all")output_file:close()ngx.header.content_type = "image/" .. extngx.print(output_data)-- 清理临时文件os.remove(input_path)os.remove(output_path)
endreturn _M
该模块定义了一个函数 process_image_with_watermark
,用于处理图片并添加水印。它将图片数据保存到临时文件,添加水印后再读取处理后的图片并返回给客户端,最后清理临时文件。
Nginx 配置文件
以下是支持代理服务和 URL 动态图片处理的 Nginx 配置:
worker_processes auto;
error_log logs/error.log debug;events {worker_connections 10240;
}http {include mime.types;default_type application/octet-stream;lua_package_path "/usr/local/openresty/lualib/?.lua;/usr/share/lua/5.1/?.lua;;";lua_package_cpath "/usr/lib64/lua/5.1/?.so;/usr/lib/lua/5.1/?.so;/usr/local/openresty/lualib/?.so;;";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 /usr/local/openresty/nginx/logs/access.log main;resolver 8.8.8.8 8.8.4.4; # 使用 Google Public DNSserver {listen 80;server_name example.com;location /users/ {content_by_lua_block {local http = require("resty.http")local image_handler = require("image_handler")local backend_url = "http://backend_service" .. ngx.var.request_urilocal httpc = http.new()local res, err = httpc:request_uri(backend_url, { method = "GET" })if not res or res.status ~= 200 thenngx.status = ngx.HTTP_BAD_GATEWAYngx.say("Failed to fetch image")returnendimage_handler.process_image_with_watermark(res.body, "jpg")}}location /process_url_image {content_by_lua_block {local http = require("resty.http")local image_handler = require("image_handler")local res, err = http.new():request_uri("http://example.com/sample_image.jpg", { method = "GET" })if not res or res.status ~= 200 thenngx.status = ngx.HTTP_BAD_GATEWAYngx.say("Failed to fetch image")returnendimage_handler.process_image_with_watermark(res.body, "jpg")}}}
}
在这个配置文件中,我们定义了两个 location
块。/users/
块用于处理根据用户和图片 ID 获取图片的请求,从后端服务获取图片后调用 image_handler
模块添加水印。/process_url_image
块用于处理从指定 URL 加载图片的请求,同样调用 image_handler
模块添加水印。
三、总结与优化方向
3.1 实现亮点
- 解耦架构:通过代理实现图片处理与应用的分离,无需修改后端代码,降低了系统的耦合度,提高了可维护性。
- 动态水印处理:支持多种图片格式,能够实时为图片添加水印,满足了业务的功能性需求。
- 高性能设计:结合 Nginx 的缓存优化,能够高效处理高并发请求,提升了系统的性能。
3.2 优化方向
- 扩展功能:进一步支持更多的图片格式和水印样式,如不同的水印位置、透明度等,以满足更复杂的业务需求。
- 自动化部署:提供一键安装脚本,简化系统的配置和部署流程,降低运维成本。
- 可视化管理:开发前端页面,允许用户动态配置水印参数,如水印文本、字体、颜色等,提高用户体验。
通过 OpenResty 的灵活性和高性能,我们可以快速实现动态图片处理功能,并显著提升系统的可扩展性和维护性。期待您在实际应用中进行更多的探索与实践!
相关文章:

使用 OpenResty 构建高效的动态图片水印代理服务20250127
使用 OpenResty 构建高效的动态图片水印代理服务 在当今数字化的时代,图片在各种业务场景中广泛应用。为了保护版权、统一品牌形象,动态图片水印功能显得尤为重要。然而,直接在后端服务中集成水印功能,往往会带来代码复杂度增加、…...

Kafka下载
一、Kafka下载 下载地址:https://kafka.apache.org/downloads 二、Kafka安装 因为选择下载的是 .zip 文件,直接跳过安装,一步到位。 选择在任一磁盘创建空文件夹(不要使用中文路径),解压之后把文件夹内容…...

【C++语言】卡码网语言基础课系列----5. A+B问题VIII
文章目录 练习题目AB问题VIII具体代码实现 小白寄语诗词共勉 练习题目 AB问题VIII 题目描述: 你的任务是计算若干整数的和。 输入描述: 输入的第一行为一个整数N,接下来N行每行先输入一个整数M,然后在同一行内输入M个整数。 输出…...

IP服务模型
1. IP数据报 IP数据报中除了包含需要传输的数据外,还包括目标终端的IP地址和发送终端的IP地址。 数据报通过网络从一台路由器跳到另一台路由器,一路从IP源地址传递到IP目标地址。每个路由器都包含一个转发表,该表告诉它在匹配到特定目标地址…...

仿真设计|基于51单片机的温湿度、一氧化碳、甲醛检测报警系统
目录 具体实现功能 设计介绍 51单片机简介 资料内容 仿真实现(protues8.7) 程序(Keil5) 全部内容 资料获取 具体实现功能 (1)温湿度传感器、CO传感器、甲醛传感器实时检测温湿度值、CO值和甲醛值进…...

QModbusTCPClient 服务器断开引起的程序崩溃
最近使用QModbusTCPClient 与一套设备通信,有一个QTimer频繁的通过读取设备寄存器。程序运行良好,但是有个问题:正常进行中设备断电了,整个程序都会崩溃。解决过程如下: 1.失败方案一 在QModbusTCPClient的errorOccu…...

Vue 3 30天精进之旅:Day 11 - 状态管理
在开发复杂的前端应用时,状态管理是一个不可或缺的部分。Vuex是Vue.js官方提供的状态管理库,能够高效地管理应用中的共享状态。它使得各个组件之间的状态共享和维护变得更加简单和清晰。今天,我们将探讨以下几个方面: Vuex概述安…...

npm 和 pip 安装中常见问题总结
安装路径的疑惑:NPM 和 PIP 的安装机制 NPM 安装路径规则: 依赖安装在项目目录下: 当你运行 npm install --save-dev jest,它会在当前目录(例如 F:\)下创建一个 node_modules 文件夹,把 jest 安…...

Flutter开发环境配置
下载 Flutter SDK 下载地址:https://docs.flutter.cn/get-started/install M1/M2芯片选择带arm64字样的Flutter SDK。 解压 cd /Applications unzip ~/Downloads/flutter_macos_arm64_3.27.3-stable.zip执行 /Applications/flutter/bin/flutterManage your Flut…...

Two Divisors ( Educational Codeforces Round 89 (Rated for Div. 2) )
Two Divisors ( Educational Codeforces Round 89 (Rated for Div. 2) ) You are given n n n integers a 1 , a 2 , … , a n a_1, a_2, \dots, a_n a1,a2,…,an. For each a i a_i ai find its two divisors d 1 > 1 d_1 > 1 d1…...

亚博microros小车-原生ubuntu支持系列:17 gmapping
前置依赖 先看下亚博官网的介绍 Gmapping简介 gmapping只适用于单帧二维激光点数小于1440的点,如果单帧激光点数大于1440,那么就会出【[mapping-4] process has died】 这样的问题。 Gmapping是基于滤波SLAM框架的常用开源SLAM算法。 Gmapping基于RBp…...

Java面试题2025-并发编程进阶(线程池和并发容器类)
线程池 一、什么是线程池 为什么要使用线程池 在开发中,为了提升效率的操作,我们需要将一些业务采用多线程的方式去执行。 比如有一个比较大的任务,可以将任务分成几块,分别交给几个线程去执行,最终做一个汇总就可…...

Stable Diffusion 3.5 介绍
Stable Diffusion 3.5 是由 Stability AI 推出的最新一代图像生成模型,是 Stable Diffusion 系列的重要升级版本。以下是关于 Stable Diffusion 3.5 的详细信息: 版本概述 Stable Diffusion 3.5 包含三个主要版本: Stable Diffusion 3.5 L…...

云计算部署模式全面解析
目录 引言公有云私有云混合云三种部署模式的对比选择建议未来趋势结语 1. 引言 随着云计算技术的快速发展,企业在选择云部署模式时面临着多种选择。本文将深入探讨云计算的三种主要部署模式:公有云、私有云和混合云,帮助读者全面了解它们的特点、优势及适用场景。 © iv…...

Vue 与 Electron 结合开发桌面应用
1. 引言 1.1 什么是 Electron? Electron 是一个使用 JavaScript、HTML 和 CSS 构建跨平台桌面应用程序的框架。它结合了 Chromium 渲染引擎和 Node.js 运行时,使得开发者可以使用 Web 技术创建原生桌面应用。1.2 为什么选择 Vue.js 和 Electron? Vue.js 是一个渐进式 JavaSc…...

数据库优化:提升性能的关键策略
1. 引言 在后端开发中,数据库的性能直接影响系统的稳定性和响应速度。随着业务增长,数据库查询变慢、负载过高等问题可能会影响用户体验。 本文将介绍数据库优化的关键策略,包括索引优化、查询优化、分库分表、缓存机制等,并结合…...

使用openAI与Deepseek的感受
今天简单介绍下使用OpenAI和DeepSeek的感觉,有些地方可能存在不准确的地方,望指正: 从2023年的秋冬到现在2025年的1月间,OpenAI和DeepSeek我都用它们来帮我,当然更多的是OpenAI,但整体感受如下:…...

pytorch实现长短期记忆网络 (LSTM)
人工智能例子汇总:AI常见的算法和例子-CSDN博客 LSTM 通过 记忆单元(cell) 和 三个门控机制(遗忘门、输入门、输出门)来控制信息流: 记忆单元(Cell State) 负责存储长期信息&…...

【ubuntu】双系统ubuntu下一键切换到Windows
ubuntu下一键切换到Windows 1.4.1 重启脚本1.4.2 快捷方式1.4.3 移动快捷方式到系统目录 按前文所述文档,开机默认启动ubuntu。Windows切换到Ubuntu直接重启就行了,而Ubuntu切换到Windows稍微有点麻烦。可编辑切换重启到Windows的快捷方式。 1.4.1 重启…...

【PyTorch】6.张量形状操作:在深度学习的 “魔方” 里,玩转张量形状
目录 1. reshape 函数的用法 2. transpose 和 permute 函数的使用 4. squeeze 和 unsqueeze 函数的用法 5. 小节 个人主页:Icomi 专栏地址:PyTorch入门 在深度学习蓬勃发展的当下,PyTorch 是不可或缺的工具。它作为强大的深度学习框架&am…...

大模型GUI系列论文阅读 DAY4续:《Large Language Model Agent for Fake News Detection》
摘要 在当前的数字时代,在线平台上虚假信息的迅速传播对社会福祉、公众信任和民主进程构成了重大挑战,并影响着关键决策和公众舆论。为应对这些挑战,自动化假新闻检测机制的需求日益增长。 预训练的大型语言模型(LLMs࿰…...

论文阅读(九):通过概率图模型建立连锁不平衡模型和进行关联研究:最新进展访问之旅
1.论文链接:Modeling Linkage Disequilibrium and Performing Association Studies through Probabilistic Graphical Models: a Visiting Tour of Recent Advances 摘要: 本章对概率图模型(PGMs)的最新进展进行了深入的回顾&…...

python小知识-typing注解你的程序
python小知识-typing注解你的程序 1. Typing的简介 typing 是 Python 的一个标准库,它提供了类型注解的支持,但并不会强制类型检查。类型注解在 Python 3.5 中引入,并在后续版本中得到了增强和扩展。typing 库允许开发者为变量、函数参数和…...

git基础使用--1--版本控制的基本概念
git基础使用–1–版本控制的基本概念 1.版本控制的需求背景,即为啥需要版本控制 先说啥叫版本,这个就不多说了吧,我们写代码的时候肯定不可能一蹴而就,肯定是今天写一点,明天写一点,对于项目来讲ÿ…...

“新月智能武器系统”CIWS,开启智能武器的新纪元
新月人物传记:人物传记之新月篇-CSDN博客 相关文章链接:星际战争模拟系统:新月的编程之道-CSDN博客 新月智能护甲系统CMIA--未来战场的守护者-CSDN博客 “新月之智”智能战术头盔系统(CITHS)-CSDN博客 目录 智能武…...

JVM运行时数据区域-附面试题
Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同的数据区域。这些区域 有各自的用途,以及创建和销毁的时间,有的区域随着虚拟机进程的启动而一直存在,有些区域则是 依赖用户线程的启动和结束而建立和销毁。 1. 程序计…...

增删改查(CRUD)操作
文章目录 MySQL系列:1.CRUD简介2.Create(创建)2.1单行数据全列插入2.2 单行数据指定插入2.3 多⾏数据指定列插⼊ 3.Retrieve(读取)3.1 Select查询3.1.1 全列查询3.1.2 指定列查询3.1.3 查询字段为表达式(都是临时表不会对原有表数据产生影响)…...

Vue.js `Suspense` 和异步组件加载
Vue.js Suspense 和异步组件加载 今天我们来聊聊 Vue 3 中的一个强大特性:<Suspense> 组件,以及它如何帮助我们更优雅地处理异步组件加载。如果你曾在 Vue 项目中处理过异步组件加载,那么这篇文章将为你介绍一种更简洁高效的方式。 什…...

HTB:LinkVortex[WriteUP]
目录 连接至HTB服务器并启动靶机 信息收集 使用rustscan对靶机TCP端口进行开放扫描 使用nmap对靶机TCP开放端口进行脚本、服务扫描 使用nmap对靶机TCP开放端口进行漏洞、系统扫描 使用nmap对靶机常用UDP端口进行开放扫描 使用gobuster对靶机进行路径FUZZ 使用ffuf堆靶机…...

Linux命令入门
Linux命令入门 ls命令 ls命令的作用是列出目录下的内容,语法细节如下: 1s[-a -l -h] [Linux路径] -a -l -h是可选的选项 Linux路径是此命令可选的参数 当不使用选项和参数,直接使用ls命令本体,表示:以平铺形式,列出当前工作目录下的内容 ls命令的选项 -a -a选项&a…...