java实现图片添加水印
文章目录
- 前言
- 一、工具类WatermarkUtil
- 二、工具类介绍
- 2.1 图片来源类型
- 2.2 水印类型
- 2.3 读取本地图片
- 2.4 读取网络图片
- 2.5 水印处理
- 2.6 添加水印
- 三、测试添加水印
- 总结
前言
给图片添加水印是一个很常见的需求,一般是用来防盗用。比如我们csdn上面写的文章中,如果包含图片,那么可以给图片带上个人水印标志。笔者是因为工作需要,特此研究一下使用java实现给图片加水印的方法。
一、工具类WatermarkUtil
完整的工具类代码如下
package com.hulei.watermark;import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.URL;/*** 添加水印util*/
public class WatermarkUtil {/*** 添加水印** @param pictureSourceType 图片来源类型* @param watermarkType 水印类型* @param path 图片路径* @param watermark 水印内容(文字水印内容/图片水印的存放路径)* @param formatName 图片格式* @param targetPath 输出图片存放路径* @param color 水印颜色*/public static void addWatermark(PictureSourceType pictureSourceType, WatermarkType watermarkType, String path, String watermark, String formatName, String targetPath, Color color) {if (null == pictureSourceType) {throw new RuntimeException("图片来源类型不能为空");}if (null == watermarkType) {throw new RuntimeException("水印类型不能为空");}Image image;if (pictureSourceType == PictureSourceType.LOCAL_PICTURE) {// 读取本地图片image = readLocalPicture(path);} else {// 读取网络图片image = readNetworkPicture(path);}// 水印处理manageWatermark(image, watermarkType, watermark, formatName, targetPath, color);}//图片来源类型枚举public enum PictureSourceType {//本地图片LOCAL_PICTURE,//网络图片NETWORK_PICTURE;}//水印类型枚举public enum WatermarkType {//文字水印TEXT_WATERMARK,//图片水印IMAGE_WATERMARK;}/*** 读取本地图片** @param path 本地图片存放路径*/public static Image readLocalPicture(String path) {if (null == path) {throw new RuntimeException("本地图片路径不能为空");}// 读取原图片信息 得到文件File srcImgFile = new File(path);try {// 将文件对象转化为图片对象return ImageIO.read(srcImgFile);} catch (IOException e) {throw new RuntimeException(e);}}/*** 读取网络图片** @param path 网络图片地址*/public static Image readNetworkPicture(String path) {if (null == path) {throw new RuntimeException("网络图片路径不能为空");}try {// 创建一个URL对象,获取网络图片的地址信息URL url = new URL(path);// 将URL对象输入流转化为图片对象 (url.openStream()方法,获得一个输入流)BufferedImage bugImg = ImageIO.read(url.openStream());if (null == bugImg) {throw new RuntimeException("网络图片地址不正确");}return bugImg;} catch (IOException e) {throw new RuntimeException(e);}}/*** 水印处理** @param image 图片对象* @param watermarkType 水印类型(1-文字水印 2-图片水印)* @param watermark 水印内容(文字水印内容/图片水印的存放路径)* @param formatName 图片格式* @param tarImgPath 输出图片存放路径* @param color 水印颜色*/public static void manageWatermark(Image image, WatermarkType watermarkType, String watermark, String formatName, String tarImgPath, Color color) {int imgWidth = image.getWidth(null);int imgHeight = image.getHeight(null);BufferedImage bufImg = new BufferedImage(imgWidth, imgHeight, BufferedImage.TYPE_INT_RGB);// 加水印,创建画笔Graphics2D graphics = bufImg.createGraphics();// 绘制原始图片graphics.drawImage(image, 0, 0, imgWidth, imgHeight, null);// 校验水印的类型if (watermarkType == WatermarkType.TEXT_WATERMARK) {if (watermark.isEmpty()) {throw new RuntimeException("文字水印内容不能为空");}// 添加文字水印:// 根据图片的背景设置水印颜色graphics.setColor(color == null ? Color.RED : color);// 设置字体 画笔字体样式为微软雅黑,加粗,文字大小为45ptgraphics.setFont(new Font("微软雅黑", Font.BOLD, 45));// 设置水印的坐标(为原图片中间位置)int x = (imgWidth - getWatermarkLength(watermark, graphics)) / 2;int y = imgHeight / 2;// 画出水印 第一个参数是水印内容,第二个参数是x轴坐标,第三个参数是y轴坐标graphics.drawString(watermark, x, y);graphics.dispose();} else {// 添加图片水印:if (watermark.isEmpty()) {throw new RuntimeException("图片水印存放路径不能为空");}Image srcWatermark = readLocalPicture(watermark);int watermarkWidth = srcWatermark.getWidth(null);int watermarkHeight = srcWatermark.getHeight(null);// 设置 alpha 透明度:alpha 必须是范围 [0.0, 1.0] 之内(包含边界值)的一个浮点数字graphics.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, 0.9f));// 绘制水印图片 坐标为中间位置graphics.drawImage(srcWatermark, (imgWidth - watermarkWidth) / 2, (imgHeight - watermarkHeight) / 2, watermarkWidth, watermarkHeight, null);graphics.dispose();}// 输出图片try {FileOutputStream outImgStream = new FileOutputStream(tarImgPath);ImageIO.write(bufImg, formatName, outImgStream);outImgStream.flush();outImgStream.close();} catch (IOException e) {throw new RuntimeException(e);}}/*** 获取水印文字的长度** @param watermarkContent 文字水印内容* @param graphics 图像类*/private static int getWatermarkLength(String watermarkContent, Graphics2D graphics) {return graphics.getFontMetrics(graphics.getFont()).charsWidth(watermarkContent.toCharArray(), 0, watermarkContent.length());}}
二、工具类介绍
2.1 图片来源类型
这个枚举主要包括两个待添加水印的图片来源类型,分别为本地图片和网络图片,即是给本地图片加水印还是网络图片加水印。
//图片来源类型枚举public enum PictureSourceType {//本地图片LOCAL_PICTURE,//网络图片NETWORK_PICTURE;}
2.2 水印类型
给图片加水印,水印也分为两种类型,分别为文字水印和图片水印,图片水印即是把图片作为水印添加到另外一个图片上。
//水印类型枚举public enum WatermarkType {//文字水印TEXT_WATERMARK,//图片水印IMAGE_WATERMARK;}
2.3 读取本地图片
该函数用于读取指定路径下的本地图片,并返回一个Image对象。如果路径为空,则抛出运行时异常;如果读取图片失败,则将捕获的IOException转换为运行时异常抛出。
2.4 读取网络图片
该函数用于读取网络图片。首先检查传入的网络图片地址是否为空,如果为空则抛出运行时异常。然后通过URL对象获取网络图片的输入流,并使用ImageIO的read方法将输入流转化为图片对象。如果转化失败则抛出运行时异常。最后返回转化成功的图片对象。
2.5 水印处理
该函数用于给图片添加水印,支持文字水印和图片水印两种类型。根据传入的水印类型,选择添加文字水印或图片水印。对于文字水印,会根据传入的颜色设置水印颜色,设置字体样式为微软雅黑加粗45pt,将水印内容居中显示在图片上。对于图片水印,会读取指定路径的图片,并将其居中显示在图片上。最后将添加了水印的图片输出到指定路径。
2.6 添加水印
该函数用于给图片添加水印。根据传入的图片来源类型和水印类型,读取相应的图片并进行水印处理,最后将处理后的图片保存到指定路径。具体流程如下:
- 检查传入的图片来源类型和水印类型是否为空,若为空则抛出异常。
- 根据图片来源类型,读取本地图片或网络图片。
- 调用manageWatermark方法对图片进行水印处理,处理包括水印类型、水印内容、图片格式、输出路径和水印颜色等参数。
- 完成水印处理后,函数执行结束。
public static void addWatermark(PictureSourceType pictureSourceType, WatermarkType watermarkType, String path, String watermark, String formatName, String targetPath, Color color) {if (null == pictureSourceType) {throw new RuntimeException("图片来源类型不能为空");}if (null == watermarkType) {throw new RuntimeException("水印类型不能为空");}Image image;if (pictureSourceType == PictureSourceType.LOCAL_PICTURE) {// 读取本地图片image = readLocalPicture(path);} else {// 读取网络图片image = readNetworkPicture(path);}// 水印处理manageWatermark(image, watermarkType, watermark, formatName, targetPath, color);}
三、测试添加水印
主要功能是给图片添加水印。具体来说,函数使用了WatermarkUtil工具类的addWatermark方法,实现了给本地图片和网络图片添加文字水印和图片水印,并将添加水印后的图片保存到指定路径。函数中定义了图片的本地路径、网络地址、水印内容、水印图片路径、输出格式以及水印颜色等参数。通过调用addWatermark方法,实现了给本地图片和网络图片添加文字水印和图片水印的功能。
测试类完整代码
package com.hulei.watermark;import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import java.awt.*;@SpringBootTest
class AddWaterMarkApplicationTests {@Testvoid contextLoads() {// 本地图片路径String localPath = "D:/localImage.png";// 网络图片地址String networkPath = "https://nimg.ws.126.net/?url=http%3A%2F%2Fdingyue.ws.126.net%2F2023%2F0329%2F31dc71faj00rs8saz004sd000vc015sm.jpg&thumbnail=660x2147483647&quality=80&type=jpg";// 文字水印内容String textWatermark = "Hello World!";// 图片水印路径String pictureWatermark = "D:/imageWaterMark.png";//输出图片的格式String outputFormat = "jpg";//本地图片添加文字水印输出地址String local_image_text_watermark = "E:/local_image_text_watermark.jpg";//本地图片添加图片水印输出地址String local_image_picture_watermark = "E:/local_image_picture_watermark.jpg";//网络图片添加文字水印输出地址String network_image_text_watermark = "E:/network_image_text_watermark.jpg";//网络图片添加图片水印输出地址String network_image_picture_watermark = "E:/network_image_picture_watermark.jpg";// 本地图片 添加文字水印WatermarkUtil.addWatermark(WatermarkUtil.PictureSourceType.LOCAL_PICTURE, WatermarkUtil.WatermarkType.TEXT_WATERMARK, localPath,textWatermark,outputFormat,local_image_text_watermark,Color.RED);// 本地图片 添加图片水印,图片水印不需要设置颜色WatermarkUtil.addWatermark(WatermarkUtil.PictureSourceType.LOCAL_PICTURE, WatermarkUtil.WatermarkType.IMAGE_WATERMARK, localPath, pictureWatermark,outputFormat,local_image_picture_watermark,null);// 网络图片 添加文字水印WatermarkUtil.addWatermark(WatermarkUtil.PictureSourceType.NETWORK_PICTURE, WatermarkUtil.WatermarkType.TEXT_WATERMARK, networkPath, textWatermark,outputFormat,network_image_text_watermark,Color.RED);// 网络图片 添加图片水印,图片水印不需要设置颜色WatermarkUtil.addWatermark(WatermarkUtil.PictureSourceType.NETWORK_PICTURE, WatermarkUtil.WatermarkType.IMAGE_WATERMARK, networkPath, pictureWatermark,outputFormat,network_image_picture_watermark,null);}}
注意:png图片不能是非png格式的图片通过改名和修改后缀得到,否则将会读取不到图片
这两个图片分别为本地图片和作为水印的图片,笔者放在了D盘根目录下
点击测试类方法后会在E盘得到四张不同图片,依次如下:
- 本地图片添加文字水印
- 本地图片添加图片水印
- 网络图片添加文字水印
- 网络图片添加图片水印
总结
以上就是笔者在探索使用java为图片添加水印完整过程,使用的都是java原生提供的处理图片的方式。使用的图片都是一些网上随便找的,如有侵权请评论区找我,我会及时撤下哈。
相关文章:
java实现图片添加水印
文章目录 前言一、工具类WatermarkUtil二、工具类介绍2.1 图片来源类型2.2 水印类型2.3 读取本地图片2.4 读取网络图片2.5 水印处理2.6 添加水印 三、测试添加水印总结 前言 给图片添加水印是一个很常见的需求,一般是用来防盗用。比如我们csdn上面写的文章中&#…...
CSS规则——font-face
font-face 什么是font-face? 想要让网页文字千变万化,仅靠font-family还不够,还要借助font-face(是一个 CSS 规则,它允许你在网页上使用自定义字体,而不仅仅是用户系统中预装的字体。这意味着你可以通过提…...
【单片机毕业设计选题24034】-基于STM32的手机智能充电系统
系统功能: 系统可以设置充电时长,启动充电后按设置的充电时长充电,充电时间到后自动 停止充电,中途检测到温度过高也会结束充电并开启风扇和蜂鸣器报警。 系统上电后,OLED显示“欢迎使用智能充电系统请稍后”,两秒钟…...
[C++][数据结构][图][中][图的遍历][最小生成树]详细讲解
目录 1.图的遍历1.广度优先遍历2.深度优先遍历 2.最小生成树1.Kruskal算法2.Prim算法 1.图的遍历 给定一个图G和其中任意一个顶点 v 0 v_0 v0,从 v 0 v_0 v0出发,沿着图中各边访问图中的所有顶点,且每个顶 点仅被遍历一次 “遍历”&…...
退市新规解读—财务类强制退市
一、退市风险警示:第一年触及相关指标 上市公司最近一个会计年度触及下列退市风险指标之一,公司股票或存托凭证被实施退市风险警示(*ST): 第1项 组合类财务指标 仅发行A股或B股,最近一个会计年度或追溯重述后最近一个会计年度 …...
小程序的生命周期使用方法和应用场景
小程序生命周期 初始化(App Launch) • 触发时机:小程序首次启动时。 • 主要事件:onLaunch。 • 功能与适用场景: • 全局数据初始化:设置应用的全局状态和变量。 • 登录状态检查:判断用户是…...
什么是C++模块化系统?C++20的模块化系统。
C20引入的模块化系统是一种新的代码组织和编译机制,它旨在替代传统的头文件机制,提供更好的代码组织、更快的编译速度和更强的封装性。模块化系统的主要目标包括: 减少编译时间:通过减少冗余的头文件解析和宏定义传播,…...
智慧校园-档案管理系统总体概述
智慧校园档案管理系统,作为教育信息化进程中的重要一环,它运用现代信息技术的力量,彻底改变了传统档案管理的面貌,为学校档案资源的收集、整理、存储、检索与利用开辟了全新的途径。这一系统全面覆盖学生、教职工、教学科研及行政…...
文心一言 VS 讯飞星火 VS chatgpt (290)-- 算法导论21.3 3题
三、给出一个包含 m 个 MAKE-SET 、UNION 和 FINDSET 操作的序列(其中有 n 个是 MAKE-SET 操作),当仅使用按秩合并时,需要 Ω(mlgn) 的时间。如果要写代码,请用go语言。 文心一言: 在并查集(Union-Find)数…...
逻辑回归梯度推导
逻辑回归是一种广泛使用的分类算法,用于估计一个事件发生的概率。它是线性回归的扩展,通过sigmoid函数将线性回归的输出映射到[0, 1]区间,从而用于分类任务。 在逻辑回归中,我们使用对数似然损失函数(log-likelihood l…...
Python 使用函数输出一个整数的逆序数
在Python中,你可以定义一个函数来输出一个整数的逆序数。这里有一个简单的实现方法: def reverse_integer(x):# 检查输入是否为整数if not isinstance(x, int):raise ValueError("Input must be an integer")# 将整数转换为字符串,…...
【Linux】Wmware Esxi磁盘扩容
目录 一、概述 1.1 磁盘分区概念 1.2 LVM概念 二、扩容步骤 二、报错 一、概述 1.1 磁盘分区概念 在 Linux 中,每一个硬件设备都映射到一个系统的文件,对于硬盘、光驱等 IDE 或 SCSI 设备也不例外。Linux把各种 IDE 设备分配了一个由 hd 前缀组成的文…...
树莓派4B_OpenCv学习笔记15:OpenCv定位物体实时坐标
今日继续学习树莓派4B 4G:(Raspberry Pi,简称RPi或RasPi) 本人所用树莓派4B 装载的系统与版本如下: 版本可用命令 (lsb_release -a) 查询: Opencv 版本是4.5.1: 今日学习 OpenCv定位物体实时位置,代码来源是…...
MySQL之如何定位慢查询
1、如何定位慢查询 1.1、使用开源工具 调试工具:Arthas 运维工具:Promethuss、Skywalking 1.2、MySQL自带慢日志 慢查询日志记录了所有执行时间超过指定参数(long_query_time,单位:秒,默认10秒&#x…...
Open3D 删除点云中重复的点
目录 一、算法原理1、重叠点2、主要函数二、代码实现三、结果展示本文由CSDN点云侠原创,原文链接。如果你不是在点云侠的博客中看到该文章,那么此处便是不要脸的爬虫与GPT。 一、算法原理 1、重叠点 原始点云克隆一份 构造重叠区域 合并点云获得重叠点 2、主要…...
填报志愿选专业是兴趣重要还是前景重要?
进行专业评估,找到一个适合自己的专业是一件非常困难的事情。在进行专业选择时,身上理想化色彩非常严重的人,会全然不顾及他人的劝阻,义无反顾的以兴趣为主,选择自己热爱的专业。一些较多考虑他人建议,能听…...
python开发基础——day9 函数基础与函数参数
一、初识函数(function) 编程函数!数学函数,里面的是逻辑,功能,而不是套公式 编程函数的作用实现特定操作的一段代码 你现在请客,每个人都点同样的一份吃的,请100个人 1.薯条 2.上校鸡块 3.可乐 那…...
STM32——使用TIM输出比较产生PWM波形控制舵机转角
一、输出比较简介: 只有高级定时器和通用寄存器才有输入捕获/输出比较电路,他们有四个CCR(捕获/比较寄存器),共用一个CNT(计数器),而输出比较功能是用来输出PWM波形的。 红圈部分…...
第十五章 集合(set)(Python)
文章目录 前言一、集合 前言 集合(set)是一个无序的不重复元素序列。 一、集合 set {1, 2, 3, 4}...
面试-javaIO机制
1.BIO BIO:是传统的javaIO以及部分java.net下部分接口和类。例如,socket,http等,因为网络通信同样是IO行为。传统IO基于字节流和字符流进行操作。提供了我们最熟悉的IO功能,譬如基于字节流的InputStream 和OutputStream.基于字符流…...
在.NET Core中,config和ConfigureServices的区别和作用
在.NET Core中,config和ConfigureServices是两个不同的概念,它们在应用程序的启动和配置过程中扮演着不同的角色。 ConfigureServices:这是ASP.NET Core应用程序中的一个方法,位于Startup类的内部。它的作用是配置依赖注入(DI)容器…...
App Inventor 2 如何实现多个定时功能?
1、可以使用多个“计时器”组件。 2、也可以用一个计时器,定时一分钟。也就是一分钟就会触发一次事件执行,定义一个全局数字变量,在事件中递增,用逻辑判断这个变量的值即可完成多个想要定时的任务(о∀о) 代码块请参考…...
技术驱动的音乐变革:AI带来的产业重塑
📑引言 近一个月来,随着几款音乐大模型的轮番上线,AI在音乐产业的角色迅速扩大。这些模型不仅将音乐创作的门槛降至前所未有的低点,还引发了一场关于AI是否会彻底颠覆音乐行业的激烈讨论。从初期的兴奋到现在的理性审视࿰…...
重生之我要学后端0--HTTP协议和RESTful APIs
http和RESTful APIs HTTP协议RESTful APIs设计RESTful API设计实例 HTTP协议 HTTP(超文本传输协议)是用于分布式、协作式和超媒体信息系统的应用层协议。它是网页数据通讯的基础。工作原理简述如下: 客户端请求(Request…...
深度之眼(二十八)——神经网络基础知识(三)-卷积神经网络
文章目录 一、前言二、卷积操作2.1 填充(padding)2.2 步长2.3 输出特征图尺寸计算2.4 多通道卷积 三、池化操作四、Lenet-5及CNN结构进化史4.1 Lenet-5 一、前言 卷积神经网络–AlexNet(最牛)-2012 Lenet-5-大规模商用(1989) 二、…...
AI Infra简单记录
向量数据库的作用 1. 在AI大模型训练过程中,向量数据库可以有效提升数据检索、特征提取等任务的效率。 2、在AI大模型推理过程中,向量数据库为大模型提供外挂知识库,提升模型时效性与准确性,提供缓存能力,减少调用开…...
三英战吕布 | 第5集 | 温酒斩华雄 | 竖子不足与谋 | 三国演义 | 逐鹿群雄
🙋大家好!我是毛毛张! 🌈个人首页: 神马都会亿点点的毛毛张 📌这篇博客分享的是《三国演义》文学剧本第Ⅰ部分《群雄逐鹿》的第5️⃣集《三英战吕布》的经典语句和文学剧本全集台词 文章目录 1.经典语句2.文学剧本台…...
【C语言】自定义类型:结构体
目录 1. 结构体类型的声明 1.1. 结构的一般声明 1.2. 结构的特殊声明 2. 结构体变量的创建和初始化 3. 结构体的自引用 4. 结构体内存对齐 4.1. 对其规则(面试考点) 4.2. 为什么存在内存对齐? 4.2.1. 平台原因(移植…...
算法金 | 决策树、随机森林、bagging、boosting、Adaboost、GBDT、XGBoost 算法大全
大侠幸会,在下全网同名「算法金」 0 基础转 AI 上岸,多个算法赛 Top 「日更万日,让更多人享受智能乐趣」 决策树是一种简单直观的机器学习算法,它广泛应用于分类和回归问题中。它的核心思想是将复杂的决策过程分解成一系列简单的决…...
[每周一更]-(第103期):GIT初始化子模块
文章目录 初始化和更新所有子模块分步骤操作1. 克隆包含子模块的仓库2. 初始化子模块3. 更新子模块 查看子模块状态提交子模块的更改处理子模块路径错误的问题 该问题的缘由是因为:在写某些代码的时候,仓库中有些文件夹,只提交了文件夹名称到…...
wordpress如何清除导入的模板/产品推广方案要包含哪些内容
适用场景 为了解决丢失修改的问题,更新一条记录时,希望这条记录当前没有被别人修改,实现线程安全的数据更新 实现策略 添加version,版本号 取出记录时,获取当前version 更新时,带上version 执行更新时&…...
成都微网站开发/策划推广活动方案
今天,刚好要写开源项目的后台数据了,所以学习了一下php的命名空间,收获多多,所以总结了一下这篇文章,好了,废话不多说,上代码吧我们该如何命名空间呢?<?php // 使用命名空间的关…...
企业网站视觉设计/销售怎么做
Linux下网卡名称 修改方法汇总 一、命令修改 1 2 3 4 5 6 7 8 9 10 11 ip linkset eth0 name eth01 ip linkset eth1 name eth10 ip linkset eth2 name eth23 ip linkset eth3 name eth32 ip linkset eth01 name eth1 ip linkset eth10 name eth0 ip linkset eth23 name eth3…...
wordpress backup to dropbox/中小企业管理培训课程
目录 一、前后端使用架构导致 二、开发人员经验问题/思维严谨性导致 三、业务特点导致 四、测试人员的经验缺乏导致 五、迭代周期不合理导致 六、上下游业务严重耦合导致 前言 产生bug的具体原因或许多种多样,但在bug原因分析过程中,希望能抽丝剥茧…...
护卫神 安装wordpress/百度文库官网
目录 一、client系列 二、立即执行函数 三、scroll系列 3.1 scroll系列属性 3.2 页面被卷去的头部 3.3 案例练习 - 仿淘宝固定侧边栏 四、三大系列总结 一、client系列 client系列相关属性可以用来获取元素可视区的相关信息。通过client系列相关属性可以动态的得到元素…...
深圳营销型网站建设服务/西安分类信息seo公司
一个应用,应该保持一套统一的样式,包括Button、EditText、ProgressBar、Toast、Checkbox等各种控件的样式,还包括控件间隔、文字大小和颜色、阴影等等。web的样式用css来定义,而android的样式主要则是通过shape、selector、layer-…...