什么?我上传图片到minio,返回的图片带水印!
欢迎来到我的博客,代码的世界里,每一行都是一个故事
🎏:你只管努力,剩下的交给时间
🏠 :小破站
什么?我上传图片到minio,返回的图片带水印!
-
- 整体逻辑概述
- 详细实现步骤
-
- MinIO 上传文件并触发事件
-
- 配置 MinIO 存储桶
- 接收 Webhook 请求
-
- 提取关键数据
- 下载文件到临时路径
-
- 使用 MinIO 的 Python SDK 下载文件
- 验证并处理图片
-
- 验证图片有效性
- 添加水印
- 上传加水印的文件并覆盖
-
- 添加水印后上传
- 避免重复处理
- 清理临时文件
- 7. 流程总结
- 生成图片样式
- 感谢
先给大家来个效果视频⬇️
仔细观看,当我上传图片后,返回了minio的路径,而且原图片右下角有水印了
你希望实现上传文件到 MinIO 后,通过自动化处理逻辑生成一份加水印的文件并覆盖原文件。以下是详细的实现步骤和逻辑说明:
整体逻辑概述
-
上传文件到 MinIO
- 用户将文件上传到 MinIO 中指定的存储桶。
- MinIO 触发事件通知,将文件的元信息通过 Webhook 回调发送到处理程序。
-
Webhook 处理程序
-
接收 Webhook 请求,提取文件名、存储桶名称等信息。
-
下载原始文件到本地临时路径。
-
验证文件的有效性(是否为图片)。
-
给文件添加水印,并保存覆盖原文件。
-
将加水印后的文件上传回 MinIO(覆盖原文件)。
-
记录文件已处理,避免重复操作(通过元数据标记)。
-
-
完成处理
- 文件在 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. 用户获得带水印的图片(覆盖原文件)。
生成图片样式
感谢
感谢你读到这里,说明你已经成功地忍受了我的文字考验!🎉
希望这篇文章没有让你想砸电脑,也没有让你打瞌睡。
如果有一点点收获,那我就心满意足了。
未来的路还长,愿你
遇见难题不慌张,遇见bug不抓狂,遇见好内容常回访。
记得给自己多一点耐心,多一点幽默感,毕竟生活已经够严肃了。
如果你有想法、吐槽或者想一起讨论的,欢迎留言,咱们一起玩转技术,笑对人生!😄
祝你代码无bug,生活多彩,心情常青!🚀