用Java给图片添加水印:简单几行代码实现
在日常开发中,图片水印是保护知识产权、标注来源的常用手段。Java通过 javax.imageio 和 java.awt 相关API,无需依赖第三方框架,仅用几行核心代码就能实现图片水印功能(支持文字水印/图片水印、自定义位置/透明度/旋转角度)。本文将带大家从基础实现到进阶优化,快速掌握实用的图片水印工具类。
一、核心原理
Java操作图片的核心是** BufferedImage (内存中的图片缓冲区)和 Graphics2D **(绘图上下文):
1. 通过 ImageIO.read() 读取原始图片,生成 BufferedImage 对象;
2. 调用 BufferedImage.createGraphics() 获取 Graphics2D ,用于绘制水印(文字/图片);
3. 配置水印的样式(字体、颜色、透明度、旋转角度等);
4. 将水印绘制到原始图片的指定位置;
5. 通过 ImageIO.write() 将处理后的图片写入磁盘或输出流。
二、基础实现:文字水印(最简单版本)
先实现一个极简版文字水印工具,支持自定义水印文字、位置、字体和颜色,核心代码仅10行左右:
1. 完整代码
java
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
/**
* Java图片文字水印工具(基础版)
*/
public class ImageWatermarkDemo {
public static void main(String[] args) {
// 原始图片路径、输出图片路径、水印文字
String srcImagePath = "D:/test/original.jpg";
String destImagePath = "D:/test/watermarked.jpg";
String watermarkText = "CSDN-技术干货";
try {
// 1. 读取原始图片
BufferedImage srcImage = ImageIO.read(new File(srcImagePath));
// 2. 获取绘图上下文(Graphics2D)
Graphics2D g2d = srcImage.createGraphics();
// 3. 配置水印样式(字体、颜色、透明度)
g2d.setFont(new Font("微软雅黑", Font.BOLD, 36)); // 字体:微软雅黑,加粗,36号
g2d.setColor(new Color(255, 255, 255, 128)); // 颜色:白色,透明度128(0-255,值越小越透明)
// 4. 绘制水印(x=50,y=80为水印左上角坐标)
g2d.drawString(watermarkText, 50, 80);
// 5. 释放资源
g2d.dispose();
// 6. 保存处理后的图片(格式为JPG,支持PNG/GIF等)
ImageIO.write(srcImage, "jpg", new File(destImagePath));
System.out.println("水印添加成功!输出路径:" + destImagePath);
} catch (IOException e) {
System.err.println("水印添加失败:" + e.getMessage());
e.printStackTrace();
}
}
}
2. 关键参数说明
- 透明度控制: new Color(255,255,255,128) 中最后一个参数是Alpha通道(0=完全透明,255=完全不透明),建议设为100-150,避免遮挡原图;
- 字体配置: new Font(字体名称, 样式, 字号) ,样式支持 Font.BOLD (加粗)、 Font.ITALIC (斜体)、 Font.PLAIN (普通);
- 坐标设置: drawString(文字, x, y) 中 y 是文字基线的纵坐标(不是左上角),需根据字号调整,避免文字超出图片范围。
三、进阶实现:通用水印工具类(支持文字+图片水印)
实际开发中需要更灵活的功能,比如图片水印、平铺水印、旋转水印等。下面封装一个通用工具类,支持两种水印类型,且可自定义各项参数:
1. 工具类完整代码
java
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
/**
* 通用图片水印工具类(支持文字水印/图片水印)
*/
public class WatermarkUtils {
// 水印类型枚举
public enum WatermarkType {
TEXT, IMAGE
}
/**
* 给图片添加水印
*
* @param srcImagePath 原始图片路径
* @param destImagePath 输出图片路径
* @param content 水印内容(文字水印=文字,图片水印=图片路径)
* @param type 水印类型(TEXT/IMAGE)
* @param font 文字水印字体(仅文字水印有效)
* @param color 文字水印颜色(仅文字水印有效)
* @param alpha 透明度(0-1,0=完全透明,1=完全不透明)
* @param rotate 旋转角度(0-360,顺时针为正)
* @param x 水印左上角x坐标
* @param y 水印左上角y坐标
* @param scale 水印缩放比例(图片水印有效,1=原尺寸)
* @throws IOException 图片读取/写入异常
*/
public static void addWatermark(String srcImagePath, String destImagePath,
String content, WatermarkType type, Font font,
Color color, float alpha, int rotate, int x, int y, float scale) throws IOException {
// 1. 读取原始图片
BufferedImage srcImage = ImageIO.read(new File(srcImagePath));
int srcWidth = srcImage.getWidth();
int srcHeight = srcImage.getHeight();
// 2. 创建图片缓冲区(与原图同尺寸、同类型)
BufferedImage destImage = new BufferedImage(srcWidth, srcHeight, srcImage.getType());
Graphics2D g2d = destImage.createGraphics();
// 3. 绘制原图到缓冲区
g2d.drawImage(srcImage, 0, 0, srcWidth, srcHeight, null);
// 4. 配置水印全局属性(透明度、抗锯齿)
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha)); // 透明度
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); // 抗锯齿(优化文字/图片边缘)
// 5. 根据水印类型绘制水印
if (type == WatermarkType.TEXT) {
// 文字水印
g2d.setFont(font);
g2d.setColor(color);
drawRotatedText(g2d, content, x, y, rotate); // 支持旋转
} else if (type == WatermarkType.IMAGE) {
// 图片水印
BufferedImage watermarkImage = ImageIO.read(new File(content));
int watermarkWidth = (int) (watermarkImage.getWidth() * scale);
int watermarkHeight = (int) (watermarkImage.getHeight() * scale);
drawRotatedImage(g2d, watermarkImage, x, y, rotate, watermarkWidth, watermarkHeight); // 支持旋转+缩放
}
// 6. 释放资源
g2d.dispose();
// 7. 保存图片(自动识别格式)
String format = destImagePath.substring(destImagePath.lastIndexOf(".") + 1);
ImageIO.write(destImage, format, new File(destImagePath));
}
/**
* 绘制旋转文字
*/
private static void drawRotatedText(Graphics2D g2d, String text, int x, int y, int rotate) {
AffineTransform transform = new AffineTransform();
transform.rotate(Math.toRadians(rotate), x, y); // 以(x,y)为旋转中心
g2d.setTransform(transform);
g2d.drawString(text, x, y);
}
/**
* 绘制旋转+缩放图片
*/
private static void drawRotatedImage(Graphics2D g2d, BufferedImage image, int x, int y, int rotate, int width, int height) {
AffineTransform transform = new AffineTransform();
transform.rotate(Math.toRadians(rotate), x + width / 2, y + height / 2); // 以图片中心为旋转中心
transform.scale(scale, scale); // 缩放
g2d.setTransform(transform);
g2d.drawImage(image, x, y, width, height, null);
}
// ------------------------------ 测试方法 ------------------------------
public static void main(String[] args) {
try {
// 测试1:添加文字水印(旋转45度,半透明)
addWatermark(
"D:/test/original.jpg",
"D:/test/text_watermark.jpg",
"原创内容,禁止转载",
WatermarkType.TEXT,
new Font("宋体", Font.BOLD + Font.ITALIC, 28),
new Color(0, 0, 0, 100), // 黑色半透明
0.5f,
45, // 旋转45度
100, // x坐标
200, // y坐标
1.0f // 文字水印无需缩放
);
// 测试2:添加图片水印(缩放0.5倍,不旋转)
addWatermark(
"D:/test/original.jpg",
"D:/test/image_watermark.jpg",
"D:/test/logo.png", // 水印图片路径
WatermarkType.IMAGE,
null, // 图片水印无需字体
null, // 图片水印无需颜色
0.6f, // 60%透明度
0, // 不旋转
50, // x坐标
50, // y坐标
0.5f // 缩放为原尺寸的50%
);
System.out.println("水印添加完成!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. 核心功能亮点
- 双水印类型:支持文字水印(自定义字体/颜色/旋转)和图片水印(自定义缩放/透明度);
- 抗锯齿优化:通过 RenderingHints 开启抗锯齿,避免文字或图片边缘模糊;
- 灵活旋转:可指定旋转角度,文字水印以左上角为中心,图片水印以自身中心为中心旋转;
- 自动格式识别:根据输出路径后缀自动识别图片格式(JPG/PNG/GIF等)。
四、常用扩展场景
1. 平铺水印(全屏重复显示)
如果需要在图片上全屏平铺水印(比如版权声明),只需在工具类中添加循环绘制逻辑:
java
/**
* 平铺文字水印(全屏重复)
*/
public static void addTileTextWatermark(String srcPath, String destPath, String text, Font font, Color color, float alpha) throws IOException {
BufferedImage srcImage = ImageIO.read(new File(srcPath));
int srcWidth = srcImage.getWidth();
int srcHeight = srcImage.getHeight();
Graphics2D g2d = srcImage.createGraphics();
g2d.drawImage(srcImage, 0, 0, srcWidth, srcHeight, null);
g2d.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
g2d.setFont(font);
g2d.setColor(color);
// 计算文字宽度和高度,循环平铺
FontMetrics metrics = g2d.getFontMetrics();
int textWidth = metrics.stringWidth(text);
int textHeight = metrics.getHeight();
// 横向间隔:文字宽度+50,纵向间隔:文字高度+50
for (int x = 0; x < srcWidth; x += textWidth + 50) {
for (int y = textHeight; y < srcHeight; y += textHeight + 50) {
g2d.drawString(text, x, y);
}
}
g2d.dispose();
ImageIO.write(srcImage, "jpg", new File(destPath));
}
2. 处理大图片(避免内存溢出)
如果处理超大图片(比如几MB甚至几十MB),直接读取整个图片到内存可能导致OOM,可通过 ImageIO 的 ImageReadParam 分块读取,或使用 Thumbnails (谷歌开源工具)先压缩图片再添加水印:
xml
<!-- 引入Thumbnails依赖(Maven) -->
<dependency>
<groupId>net.coobird</groupId>
<artifactId>thumbnailator</artifactId>
<version>0.4.19</version>
</dependency>
java
// 大图片水印示例(先压缩再加水印)
public static void addWatermarkForLargeImage(String srcPath, String destPath, String text) throws IOException {
Thumbnails.of(srcPath)
.scale(1.0) // 不改变原图尺寸
.outputQuality(0.9) // 保持图片质量
.watermark(Positions.BOTTOM_RIGHT, // 水印位置:右下角
new TextOverlay(text, new Font("微软雅黑", Font.BOLD, 24), Color.WHITE, 0.5f),
0.5f) // 水印透明度
.toFile(destPath);
}
五、注意事项与避坑指南
1. 图片格式兼容:JPG格式不支持透明通道,若需保留水印透明度(比如PNG水印图片),建议输出为PNG格式;
2. 字体路径问题:Linux系统可能没有Windows默认字体(如“微软雅黑”),建议使用跨平台字体(如“宋体”“Arial”),或手动引入字体文件;
3. 坐标越界问题:添加水印前需判断 x+水印宽度 和 y+水印高度 是否超过原图尺寸,避免水印被截断;
4. 性能优化:处理大量图片时,可使用线程池异步处理,同时避免重复创建 Font 、 Color 等对象(缓存复用)。
六、总结
Java原生API实现图片水印简单高效,无需依赖第三方框架,核心是通过 BufferedImage 和 Graphics2D 操作图片缓冲区。本文提供的基础版代码适合快速上手,通用工具类支持文字/图片水印、旋转、缩放等常用功能,可直接集成到项目中。
如果需要更复杂的需求(如渐变水印、动态水印位置计算),可以基于本文工具类进一步扩展。建议收藏本文,遇到图片水印需求时直接复用代码!