使用 OpenResty 构建高效的动态图片水印代理服务20250127

news2025/1/30 21:00:05

使用 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." .. ext
    local output_path = tmp_dir .. "output_image." .. ext
    local watermark_path = "/var/www/images/watermark.png"

    -- 保存图片到临时路径
    local input_file = io.open(input_path, "wb")
    if not input_file then
        ngx.log(ngx.ERR, "Failed to open file for writing: ", input_path)
        ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
        ngx.say("Failed to process image")
        return
    end
    input_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 then
        ngx.log(ngx.ERR, "Image processing failed: ", err)
        ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
        ngx.say("Failed to process image")
        return
    end

    -- 返回处理后的图片
    local output_file = io.open(output_path, "rb")
    if not output_file then
        ngx.log(ngx.ERR, "Failed to open processed image")
        ngx.status = ngx.HTTP_INTERNAL_SERVER_ERROR
        ngx.say("Failed to process image")
        return
    end
    local output_data = output_file:read("*all")
    output_file:close()
    ngx.header.content_type = "image/" .. ext
    ngx.print(output_data)

    -- 清理临时文件
    os.remove(input_path)
    os.remove(output_path)
end

return _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 DNS
    server {
        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_uri
                local httpc = http.new()
                local res, err = httpc:request_uri(backend_url, { method = "GET" })

                if not res or res.status ~= 200 then
                    ngx.status = ngx.HTTP_BAD_GATEWAY
                    ngx.say("Failed to fetch image")
                    return
                end

                image_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 then
                    ngx.status = ngx.HTTP_BAD_GATEWAY
                    ngx.say("Failed to fetch image")
                    return
                end
                image_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 的灵活性和高性能,我们可以快速实现动态图片处理功能,并显著提升系统的可扩展性和维护性。期待您在实际应用中进行更多的探索与实践!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2286597.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Elastic Agent 对 Kafka 的新输出:数据收集和流式传输的无限可能性

作者:来 Elastic Valerio Arvizzigno, Geetha Anne 及 Jeremy Hogan 介绍 Elastic Agent 的新功能:原生输出到 Kafka。借助这一最新功能,Elastic 用户现在可以轻松地将数据路由到 Kafka 集群,从而实现数据流和处理中无与伦比的可扩…

Elasticsearch 性能测试工具 Loadgen 之 002——命令行及参数详解

上一讲,我们讲解了 Loadgen 的极简部署方式、配置文件、快速使用从 0 到 1 方式。 本讲,我们主要解读一下 Loadgen 的丰富的命令行及参数含义。 有同学可能会说,上面不是介绍很清楚了吗?但,咱们还是有必要详细中文解读…

书生大模型实战营3

文章目录 L0——入门岛git基础Git 是什么?Git 中的一些基本概念工作区、暂存区和 Git 仓库区文件状态分支主要功能 Git 平台介绍GitHubGitLabGitee Git 下载配置验证下载 Git配置 Git验证 Git配置 Git常用操作Git简易入门四部曲Git其他指令 闯关任务任务1: 破冰活动…

【玩转全栈】----靓号管理系统实现

先赞后看,养成习惯。。。 目录 数据库设置 基本功能 路由器 靓号显示 靓号添加 靓号编辑 视图函数 额外功能 搜索功能 分页 一般逻辑 动态页码 上下页 首尾页 数据库设置 新建一个数据库(或者就用之前部门、用户管理的也行),用Dja…

【Attention】KV Cache

1 什么是KV Cache? 定义:KV Cache 即 Key-Value Cache,是用于加速 Transformer 模型推理长序列过程的一种技术。 核心原理:在 Transformer 的自注意力机制中,将历史输入 token 中的 Key 和 Value 缓存下来&#xff0c…

【Proteus仿真】【51单片机】多功能计算器系统设计

目录 一、主要功能 二、使用步骤 三、硬件资源 四、软件设计 五、实验现象 联系作者 一、主要功能 1、LCD1602液晶显示 2、矩阵按键​ 3、加减乘除,开方运算 4、带符号运算 5、最大 999*999 二、使用步骤 基于51单片机多功能计算器 包含:程序&…

【教学类-89-01】20250127新年篇01—— 蛇年红包(WORD模版)

祈愿在2025蛇年里, 伟大的祖国风调雨顺、国泰民安、每个人齐心协力,共同经历这百年未有之大变局时代(国际政治、AI技术……) 祝福亲友同事孩子们平安健康(安全、安全、安全)、巳巳如意! 背景需…

leetcode——二叉树的最大深度(java)

给定一个二叉树 root ,返回其最大深度。 二叉树的 最大深度 是指从根节点到最远叶子节点的最长路径上的节点数。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:3 示例 2: 输入:root [1,null,2] 输…

Java创建项目准备工作

新建项目 新建空项目 每一个空项目创建好后都要检查jdk版本 检查SDK和语言级别——Apply——OK 检查当前项目的Maven路径,如果已经配置好全局,就是正确路径不用管 修改项目字符集编码,将所有编码都调整为UTF-8 创建Spingboot工程 创建Spring…

汽车免拆诊断案例 | 2007 款日产天籁车起步加速时偶尔抖动

故障现象  一辆2007款日产天籁车,搭载VQ23发动机(气缸编号如图1所示,点火顺序为1-2-3-4-5-6),累计行驶里程约为21万km。车主反映,该车起步加速时偶尔抖动,且行驶中加速无力。 图1 VQ23发动机…

【JavaEE】_MVC架构与三层架构

目录 1. MVC架构 2. 三层架构 3. MVC架构与三层架构的对比 3.1 MVC与三层架构的对比 3.2 MVC与三层架构的共性 1. MVC架构 在前文已介绍关于SpringMAC的设计模式,详见下文: 【JavaEE】_Spring Web MVC简介-CSDN博客文章浏览阅读967次,点…

单片机基础模块学习——PCF8591芯片

一、A/D、D/A模块 A——Analog 模拟信号:连续变化的信号(很多传感器原始输出的信号都为此类信号)D——Digital 数字信号:只有高电平和低电平两种变化(单片机芯片、微控制芯片所能处理的都是数字信号) 下面是模拟信号和连续信号的区别 为什么需要进行模拟信号和数字信号之…

Vue5---

目录 一、学习目标 1.自定义指令 2.插槽 3.综合案例:商品列表 4.路由入门 二、自定义指令 1.指令介绍 2.自定义指令 3.自定义指令的语法 三、自定义指令-指令的值 1.需求 2.语法 3.代码示例 五、插槽-默认插槽 1.作用 2.需求 4.使用插槽的基本语法…

分享| RL-GPT 框架通过慢agent和快agent结合提高AI解决复杂任务的能力-Arxiv

结论 “RL-GPT: Integrating Reinforcement Learning and Code-as-policy” RL-GPT 框架为解决大语言模型在复杂任务处理中的难题提供了创新有效的途径, 旨在将强化学习(RL)和代码即策略相结合, 以解决大语言模型&#xff08…

Prompt提示词完整案例:让chatGPT成为“书单推荐”的高手

大家好,我是老六哥,我正在共享使用AI提高工作效率的技巧。欢迎关注我,共同提高使用AI的技能,让AI成功你的个人助理。 许多人可能会跟老六哥一样,有过这样的体验:当我们遇到一个能力出众或对事物有独到见解的…

【开源免费】基于SpringBoot+Vue.JS在线考试学习交流网页平台(JAVA毕业设计)

本文项目编号 T 158 ,文末自助获取源码 \color{red}{T158,文末自助获取源码} T158,文末自助获取源码 目录 一、系统介绍二、数据库设计三、配套教程3.1 启动教程3.2 讲解视频3.3 二次开发教程 四、功能截图五、文案资料5.1 选题背景5.2 国内…

如何解压rar格式文件?8种方法(Win/Mac/手机/网页端)

RAR 文件是一种常见的压缩文件格式,由尤金・罗谢尔(Eugene Roshal)开发,因其扩展名 “rar” 而得名。它通过特定算法将一个或多个文件、文件夹进行压缩,大幅减小存储空间,方便数据传输与备份。然而&#xf…

Kafka 副本机制(包含AR、ISR、OSR、HW 和 LEO 介绍)

文章目录 Kafka 副本机制(包含AR、ISR、OSR、HW 和 LEO 介绍)1. 副本的基本概念2. 副本同步和一致性2.1 AR(Assigned Replicas)2.2 ISR(In-Sync Replicas)2.3 OSR(Out-of-Sync Replicas&#xf…

网关登录校验

网关登录校验 单体架构时我们只需要完成一次用户登录、身份校验,就可以在所有业务中获取到用户信息。而微服务拆分后,每个微服务都独立部署,不再共享数据。也就意味着每个微服务都需要做登录校验,这显然不可取。 鉴权思路分析 …

【C语言】在Windows上为可执行文件.exe添加自定义图标

本文详细介绍了在 Windows 环境下,如何为使用 GCC 编译器编译的 C程序 添加自定义图标,从而生成带有图标的 .exe 可执行文件。通过本文的指导,读者可以了解到所需的条件以及具体的操作步骤,使生成的程序更具专业性和个性化。 目录 1. 准备条件2. 具体步骤步骤 1: 准备资源文…