最近学习了下Java实现图片添加水印,包括文字水印和图片水印,单图片添加水印和多图片批量添加水印等。下面简单总结下开发过程:
1.首先搭建框架,这里使用的是struts2框架,允许一次上传多个图片,可以将原图和添加过水印的图片对比展示。
web.xml中的配置文件:
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
struts.xml配置文件:
<struts>
<!--设置开发模式,请求后缀的配置 -->
<constant name="struts.action.extension" value="action" />
<!--设置编码形式为UTF-8 -->
<constant name="struts.i18n.encoding" value="UTF-8" />
<!--省略其他配置信息 -->
<constant name="struts.multipart.maxSize" value="1073741824" />
<!-- 设置上传文件的临时文件夹 -->
<constant name="struts.multipart.saveDir" value="/tmpUpload" />
<package name="default" extends="struts-default">
<action name="watermark" class="com.watermark.WaterMarkAction" method="watermark">
<param name="uploadPath">/images</param>
<result name="success">watermark.jsp</result>
</action>
</package>
</struts>
2.实现图片添加水印的实现思路是:
①创建缓存图片对象;
②创建Java绘图工具类;
③使用绘图工具对象将原图绘制到缓存图片对象;
④使用绘图工具将水印(文字/图片)绘制到缓存图片对象;
⑤创建图像编码工具类;
⑥使用图像编码工具类,输出缓存图像到目标图片文件。
3.给单个图片添加文字水印
public interface MarkService {
public static final String MARK_TEXT = "Byron";
public static final String FONT_NAME = "微软雅黑";
public static final int FONT_STYLE = Font.BOLD; //黑体
public static final int FONT_SIZE = 20; //文字大小
public static final Color FONT_COLOR = Color.red;//文字颜色
public static final int X = 10; //文字坐标
public static final int Y = 10;
public static float ALPHA = 0.3F; //文字水印透明度
public static final String LOGO = "logo.gif"; //图片形式的水印
public String watermark(File image,String imageFileName,String uploadPath,String realUploadPath);
}
/*
* 实现添加单个文字水印
*/
public class TextMarkService implements MarkService{
@Override
public String watermark(File image, String imageFileName,
String uploadPath, String realUploadPath) {
String logoFileName = "logo_"+imageFileName; //定义目标文件输出的名称
OutputStream os = null;
try {
//1 创建图片缓存对象
Image image2 = ImageIO.read(image); //解码原图
int width = image2.getWidth(null); //获取原图的宽度
int height = image2.getHeight(null);//获取原图的高度
BufferedImage bufferedImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB); //图像颜色的设置
//2 创建Java绘图工具对象
Graphics2D g = bufferedImage.createGraphics();
//3 使用绘图工具对象将原图绘制到缓存图片对象
g.drawImage(image2, 0, 0, width, height, null);
//4 使用绘图工具对象将水印(文字/图片)绘制到缓存图片
g.setFont(new Font(FONT_NAME,FONT_STYLE,FONT_SIZE));
g.setColor(FONT_COLOR);
//获取文字水印的宽度和高度值
int width1 = FONT_SIZE*getTextLength(MARK_TEXT);//文字水印宽度
int height1= FONT_SIZE; //文字水印高度
//计算原图和文字水印的宽度和高度之差
int widthDiff = width - width1; //宽度之差
int heightDiff= height - height1; //高度之差
int x = X; //横坐标
int y = Y; //纵坐标
//保证文字水印在右下方显示
if(x > widthDiff){
x = widthDiff;
}
if(y > heightDiff){
y = heightDiff;
}
//水印透明度的设置
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,ALPHA));
//绘制文字
g.drawString(MARK_TEXT, x, y + FONT_SIZE);
//释放工具
g.dispose();
//创建文件输出流,指向最终的目标文件
os = new FileOutputStream(realUploadPath+"/"+logoFileName);
//5 创建图像文件编码工具类
JPEGImageEncoder en = JPEGCodec.createJPEGEncoder(os);
//6 使用图像编码工具类,输出缓存图像到目标文件
en.encode(bufferedImage);
} catch (Exception e) {
e.printStackTrace();
}finally{
if(os!=null){
try {
os.close(); //关闭流
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
return uploadPath+"/"+logoFileName;
}
//文本长度的处理:文字水印的中英文字符的宽度转换
public int getTextLength(String text){
int length = text.length();
for(int i=0;i<text.length();i++){
String s = String.valueOf(text.charAt(i));
if(s.getBytes().length>1){ //中文字符
length++;
}
}
length = length%2 == 0?length/2:length/2+1; //中文和英文字符的转换
return length;
}
}
4.新建一个index.jsp页面和水印添加成功后的watermark.jsp
index.jsp页面:
<body>
<h4>上传图片</h4>
<hr />
<form name="form1" action="${pageContext.request.contextPath}/watermark.action"
method="post" enctype="multipart/form-data">
<input type="file" name="image" /><br />
<input type="file" name="image" /><br />
<input type="file" name="image" /><br />
<input type="file" name="image" /><br />
<input type="file" name="image" /><br /><br />
<input type="submit" value="上传图片" />
</form>
<hr />
</body>
效果如下:
watermark.jsp页面:
<body>
<table width="99%" align="center">
<tr>
<td width="50%" align="center">
<img src=${pageContext.request.contextPath }<s:property value="picInfo.imageURL"/> width="550" />
</td>
<td width="50%" align="center">
<img src=${pageContext.request.contextPath }<s:property value="picInfo.logoImageURL"/> width="550" />
</td>
</tr>
</table>
</body>
5.WaterMarkAction处理类
package com.watermark;
import java.io.File;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
/**
*
* description:控制器
* @author Byron
*
*/
public class WaterMarkAction extends ActionSupport {
private File image; // 单个图片上传
private String imageFileName;
private PicInfo picInfo = new PicInfo(); // 创建数据模型
private String uploadPath; // 文件上传的相对路径,要在struts.xml文件中配置
/*
* 请求处理方法:负责接收图片上传以及水印添加的请求
*/
public String watermark() throws Exception {
/*
* 单个图片上传处理逻辑
*/
// 文件上传的绝对路径是基于相对路径获取的
String realUploadPath = ServletActionContext.getServletContext()
.getRealPath(uploadPath);
UploadService uploadService = new UploadService();
// 调用uploadService来完成图片上传
picInfo.setImageURL(uploadService.uploadImage(image, imageFileName,
uploadPath, realUploadPath));
MarkService markService = new TextMarkService(); // 创建上传单个图片添加文字水印服务
// 将上传好之后图片的路径保存到数据变量中,会将picInfo变量返回到watermark.jsp页面中
picInfo.setLogoImageURL(markService.watermark(image, imageFileName,
uploadPath, realUploadPath));
return SUCCESS;
}
public File getImage() {
return image;
}
public String getImageFileName() {
return imageFileName;
}
public void setImageFileName(String imageFileName) {
this.imageFileName = imageFileName;
}
public void setImage(File image) {
this.image = image;
}
public String getUploadPath() {
return uploadPath;
}
public void setUploadPath(String uploadPath) {
this.uploadPath = uploadPath;
}
public PicInfo getPicInfo() {
return picInfo;
}
public void setPicInfo(PicInfo picInfo) {
this.picInfo = picInfo;
}
}
效果如下:
6.给图片添加多个文字水印,新建一个添加多文字水印的服务类
/*
* 实现添加多个文字水印
*/
public class MoreTextMarkService implements MarkService{
@Override
public String watermark(File image, String imageFileName,
String uploadPath, String realUploadPath) {
String logoFileName = "logo_"+imageFileName; //定义目标文件输出的名称
OutputStream os = null;
try {
//1 创建图片缓存对象
Image image2 = ImageIO.read(image);
int width = image2.getWidth(null);
int height = image2.getHeight(null);
BufferedImage bufferedImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//2 创建Java绘图工具对象
Graphics2D g = bufferedImage.createGraphics();
//3 使用绘图工具对象将原图绘制到缓存图片对象
g.drawImage(image2, 0, 0, width, height, null);
//4 使用绘图工具对象将水印(文字/图片)绘制到缓存图片
g.setFont(new Font(FONT_NAME,FONT_STYLE,FONT_SIZE));
g.setColor(FONT_COLOR);
int width1 = FONT_SIZE*getTextLength(MARK_TEXT);//文字水印宽度
int height1= FONT_SIZE; //文字水印高度
//透明度的设置
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,ALPHA));
//旋转图片(30°)
g.rotate(Math.toRadians(30),bufferedImage.getWidth()/2,bufferedImage.getHeight()/2);
//设置水印的坐标
int x= -width/2;
int y= -height/2;
while(x < width*1.5){
y = -height/2;
while(y < height*1.5){
g.drawString(MARK_TEXT,x,y);
y += height1 + 50;
}
x += width1 + 50; //水印之间的间隔设为50
}
//释放工具
g.dispose();
//最终目标文件
os = new FileOutputStream(realUploadPath+"/"+logoFileName);
//5 创建图像文件编码工具类
JPEGImageEncoder en = JPEGCodec.createJPEGEncoder(os);
//6 使用图像编码工具类,输出缓存图像到目标文件
en.encode(bufferedImage);
} catch (Exception e) {
e.printStackTrace();
}finally{
if(os!=null){
try {
os.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
return uploadPath+"/"+logoFileName;
}
//处理文字水印的中英文字符的宽度转换
public int getTextLength(String text){
int length = text.length();
for(int i=0;i<text.length();i++){
String s = String.valueOf(text.charAt(i));
if(s.getBytes().length>1){ //中文字符
length++;
}
}
length = length%2 == 0?length/2:length/2+1; //中文和英文字符的转换
return length;
}
然后修改WaterMarkAction 中 markService为:
MarkService markService = new MoreTextMarkService(); //创建上传多个图片添加文字水印服务
效果如下:
7.批量上传图片添加多个文字水印,修改WaterMarkAction
package com.watermark;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
/**
*
* description:控制器
* @author Byron
*
*/
public class WaterMarkAction extends ActionSupport {
private File[] image; // 多个图片上传
private String[] imageFileName;
private List<PicInfo> picInfo = new ArrayList<PicInfo>();
private String uploadPath; // 文件上传的相对路径,struts.xml文件中配置
/*
* 请求处理方法:负责接收图片上传以及水印添加的请求
*/
public String watermark() throws Exception {
/*
* 批量添加文字水印
*/
String realUploadPath = ServletActionContext.getServletContext()
.getRealPath(uploadPath);
UploadService uploadService = new UploadService();
// 创建水印服务类
MarkService markService = new MoreTextMarkService();
// 判断是否为多图片上传
if (image != null && image.length > 0) {
for (int i = 0; i < image.length; i++) {
PicInfo pi = new PicInfo();
pi.setImageURL(uploadService.uploadImage(image[i], imageFileName[i],
uploadPath, realUploadPath));
// 水印添加操作
pi.setLogoImageURL(markService.watermark(image[i], imageFileName[i],
uploadPath, realUploadPath));
// 将有水印的图片添加的picInfo数组中
picInfo.add(pi);
}
}
return SUCCESS;
}
public File[] getImage() {
return image;
}
public void setImage(File[] image) {
this.image = image;
}
public String[] getImageFileName() {
return imageFileName;
}
public void setImageFileName(String[] imageFileName) {
this.imageFileName = imageFileName;
}
public String getUploadPath() {
return uploadPath;
}
public void setUploadPath(String uploadPath) {
this.uploadPath = uploadPath;
}
public List<PicInfo> getPicInfo() {
return picInfo;
}
public void setPicInfo(List<PicInfo> picInfo) {
this.picInfo = picInfo;
}
}
效果如下:
8.下面介绍如何给图片添加多个图片水印:
首先创建一个服务类:
/*
* 实现添加多个图片水印
*/
public class MoreImageMarkService implements MarkService{
@Override
public String watermark(File image, String imageFileName,
String uploadPath, String realUploadPath) {
String logoFileName = "logo_"+imageFileName; //定义目标文件输出的名称
OutputStream os = null;
try {
//1 创建图片缓存对象
Image image2 = ImageIO.read(image);
int width = image2.getWidth(null);
int height = image2.getHeight(null);
BufferedImage bufferedImage = new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
//2 创建Java绘图工具对象
Graphics2D g = bufferedImage.createGraphics();
//3 使用绘图工具对象将原图绘制到缓存图片对象
g.drawImage(image2, 0, 0, width, height, null);
//4 使用绘图工具对象将水印(文字/图片)绘制到缓存图片
String logoPath = realUploadPath + "/" + LOGO;
File logo = new File(logoPath);
Image logoImage = ImageIO.read(logo);
int width1 = logoImage.getWidth(null);
int height1= logoImage.getHeight(null);
//透明度的设置
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP,ALPHA));
//旋转图片(30°)
g.rotate(Math.toRadians(30),bufferedImage.getWidth()/2,bufferedImage.getHeight()/2);
int x= -width/2;
int y= -height/2;
while(x < width*1.5){
y = -height/2;
while(y < height*1.5){
g.drawImage(logoImage,x,y,null);
y+=height1+50;
}
x+= width1 + 50;
}
g.dispose();
//创建文件输出流,指向最终的目标文件
os = new FileOutputStream(realUploadPath+"/"+logoFileName);
//5 创建图像文件编码工具类
JPEGImageEncoder en = JPEGCodec.createJPEGEncoder(os);
//6 使用图像编码工具类,输出缓存图像到目标文件
en.encode(bufferedImage);
} catch (Exception e) {
e.printStackTrace();
}finally{
if(os!=null){
try {
os.close();
} catch (Exception e2) {
e2.printStackTrace();
}
}
}
return uploadPath+"/"+logoFileName;
}
}
然后修改WaterMarkAction:
package com.watermark;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.struts2.ServletActionContext;
import com.opensymphony.xwork2.ActionSupport;
/**
*
* description:控制器
* @author Byron
*
*/
public class WaterMarkAction extends ActionSupport {
private File[] image; // 多个图片上传
private String[] imageFileName;
private List<PicInfo> picInfo = new ArrayList<PicInfo>();
private String uploadPath; // 文件上传的相对路径,要在struts.xml文件中配置
/*
* 请求处理方法:负责接收图片上传以及水印添加的请求
*/
public String watermark() throws Exception {
/*
* 批量添加图片水印
*/
String realUploadPath = ServletActionContext.getServletContext()
.getRealPath(uploadPath);
UploadService uploadService = new UploadService();
// 创建水印服务类
MarkService markService = new MoreImageMarkService();
// 判断是否为多图片上传
if (image != null && image.length > 0) {
for (int i = 0; i < image.length; i++) {
PicInfo pi = new PicInfo();
pi.setImageURL(uploadService.uploadImage(image[i], imageFileName[i],
uploadPath, realUploadPath));
// 水印添加操作
pi.setLogoImageURL(markService.watermark(image[i], imageFileName[i],
uploadPath, realUploadPath));
// 将有水印的图片添加的picInfo数组中
picInfo.add(pi);
}
}
return SUCCESS;
}
public File[] getImage() {
return image;
}
public void setImage(File[] image) {
this.image = image;
}
public String[] getImageFileName() {
return imageFileName;
}
public void setImageFileName(String[] imageFileName) {
this.imageFileName = imageFileName;
}
public String getUploadPath() {
return uploadPath;
}
public void setUploadPath(String uploadPath) {
this.uploadPath = uploadPath;
}
public List<PicInfo> getPicInfo() {
return picInfo;
}
public void setPicInfo(List<PicInfo> picInfo) {
this.picInfo = picInfo;
}
}
效果如下: