> 技术文档 > 什么?我上传图片到minio,返回的图片带水印!

什么?我上传图片到minio,返回的图片带水印!

欢迎来到我的博客,代码的世界里,每一行都是一个故事

在这里插入图片描述

🎏:你只管努力,剩下的交给时间

🏠 :小破站

什么?我上传图片到minio,返回的图片带水印

    • 整体逻辑概述
    • 详细实现步骤
      • MinIO 上传文件并触发事件
        • 配置 MinIO 存储桶
      • 接收 Webhook 请求
        • 提取关键数据
      • 下载文件到临时路径
        • 使用 MinIO 的 Python SDK 下载文件
      • 验证并处理图片
        • 验证图片有效性
        • 添加水印
      • 上传加水印的文件并覆盖
        • 添加水印后上传
        • 避免重复处理
      • 清理临时文件
      • 7. 流程总结
    • 生成图片样式
    • 感谢

先给大家来个效果视频⬇️

什么?我上传图片到minio,返回的图片带水印!

仔细观看,当我上传图片后,返回了minio的路径,而且原图片右下角有水印了

你希望实现上传文件到 MinIO 后,通过自动化处理逻辑生成一份加水印的文件并覆盖原文件。以下是详细的实现步骤和逻辑说明:

整体逻辑概述

  1. 上传文件到 MinIO

    • 用户将文件上传到 MinIO 中指定的存储桶。
    • MinIO 触发事件通知,将文件的元信息通过 Webhook 回调发送到处理程序。
  2. Webhook 处理程序

    • 接收 Webhook 请求,提取文件名、存储桶名称等信息。

    • 下载原始文件到本地临时路径。

    • 验证文件的有效性(是否为图片)。

    • 给文件添加水印,并保存覆盖原文件。

    • 将加水印后的文件上传回 MinIO(覆盖原文件)。

    • 记录文件已处理,避免重复操作(通过元数据标记)。

  3. 完成处理

    • 文件在 MinIO 中覆盖为加水印版本,完成流程。

详细实现步骤

MinIO 上传文件并触发事件

配置 MinIO 存储桶

为 MinIO 的目标存储桶启用事件通知功能(Webhook)。

运行以下命令启用 Webhook:

 mc admin config set myminio notify_webhook:webhook \\ enable=on \\ endpoint=http://:9002/webhook \\ queue_limit=1000

其中这个9002是你服务的端口

接收 Webhook 请求

提取关键数据

Webhook 请求会发送事件数据,提取其中的存储桶名称、文件键名(key)和事件类型:

data = request.get_json()record = data.get(\"Records\", [])[0]bucket_name = record.get(\"s3\", {}).get(\"bucket\", {}).get(\"name\")file_key = record.get(\"s3\", {}).get(\"object\", {}).get(\"key\")event_name = record.get(\"eventName\")if event_name != \"s3:ObjectCreated:Put\": \\# 忽略非上传事件 return jsonify({\"status\": \"ignored\"}), 200

下载文件到临时路径

使用 MinIO 的 Python SDK 下载文件

通过 fget_object 下载文件到本地临时路径。文件名根据文件键的扩展名设置:

with tempfile.NamedTemporaryFile(delete=False, suffix=os.path.splitext(file_key)[1]) as tmp_file: tmp_file_path = tmp_file.name minio_client.fget_object(bucket_name, file_key, tmp_file_path)

验证并处理图片

验证图片有效性

检查文件是否为有效图片(支持的格式、完整性等),使用 Pillow 库的 Image.verify() 方法:

def validate_image(file_path): try: mime_type, _ = mimetypes.guess_type(file_path) if not mime_type or not mime_type.startswith(\"image/\"): raise ValueError(\"File is not a valid image.\") with Image.open(file_path) as img: img.verify() return True except Exception as e: raise ValueError(f\"Invalid image: {e}\")
添加水印

打开图片后,使用 Pillow 在右下角绘制水印。水印颜色根据图片亮度动态调整(浅色背景用深色水印,反之亦然):

def add_watermark(image_path, watermark_text): with Image.open(image_path) as img: width, height = img.size draw = ImageDraw.Draw(img) \\# 加载字体 font = ImageFont.truetype(FONT_PATH, 20) \\# 根据亮度选择水印颜色 avg_brightness = calculate_average_brightness(img) text_color = (255, 255, 255, 128) if avg_brightness < 128 else (0, 0, 0, 128) \\# 计算水印文本位置 text_width, text_height = draw.textbbox((0, 0), watermark_text, font=font)[2:4] position = (width - text_width - 10, height - text_height - 10) \\# 绘制水印 draw.text(position, watermark_text, font=font, fill=text_color) \\# 覆盖保存 img.save(image_path) return image_path

上传加水印的文件并覆盖

添加水印后上传

将处理后的文件上传到 MinIO 覆盖原文件,并通过元数据标记文件已处理过:

minio_client.fput_object( bucket_name, file_key, # 覆盖原文件 watermarked_file_path, # 带水印的文件路径 content_type=\"image/png\", # 假设所有图片均为 PNG metadata={\"X-Amz-Meta-Watermarked\": \"true\"} # 标记文件已加水印)logging.info(f\"Uploaded watermarked file: {file_key}\")
避免重复处理

在每次处理前,通过读取文件元数据检查是否已加过水印:

obj_info = minio_client.stat_object(bucket_name, file_key)if \"X-Amz-Meta-Watermarked\" in obj_info.metadata: logging.info(f\"File {file_key} already watermarked. Skipping.\") return jsonify({\"status\": \"ignored\"}), 200

清理临时文件

在任务完成或失败时,清理下载的临时文件以节省磁盘空间:

finally: for path in [tmp_file_path, watermarked_file_path]: if path and os.path.exists(path): os.remove(path)

7. 流程总结

​ 1. 用户上传图片到 MinIO。

​ 2. MinIO 触发 Webhook,调用处理程序。

3.Webhook 处理程序: - 下载文件到本地。 - 验证图片并添加水印。 - 将处理后的图片覆盖上传到 MinIO。 - 添加元数据标记,防止重复处理。

​ 4. 用户获得带水印的图片(覆盖原文件)。

生成图片样式

什么?我上传图片到minio,返回的图片带水印!

感谢

感谢你读到这里,说明你已经成功地忍受了我的文字考验!🎉
希望这篇文章没有让你想砸电脑,也没有让你打瞌睡。
如果有一点点收获,那我就心满意足了。

未来的路还长,愿你
遇见难题不慌张,遇见bug不抓狂,遇见好内容常回访
记得给自己多一点耐心,多一点幽默感,毕竟生活已经够严肃了。

如果你有想法、吐槽或者想一起讨论的,欢迎留言,咱们一起玩转技术,笑对人生!😄

祝你代码无bug,生活多彩,心情常青!🚀
什么?我上传图片到minio,返回的图片带水印!