JAVA图片水印开发案例详解
写在最前面
上周零零碎碎花了一周的时间研究水印的开发,现在终于写了个入门级的demo,做下笔记同时分享出来供大家参考。
demo是在我上次写的 java实用案例之文件导入导出(poi方式) 框架基础上搭建的,基于spring+springmvc。如果有错误还请大家指正。
最后源码地址在: 。
简单介绍
水印开发是web开发中一种比较常见的功能,实现的代码很简单,具体的实现步骤我也会以代码为基础详细讲述。其实以我个人的理解,我把水印的类型和开发流程分为以下几种。
水印的类型:
单文字水印
单图片水印
多文字水印
多图片水印
水印的开发流程:
- 创建图片缓存对象
- 创建java绘图工具对象
- 使用绘图工具工具对象将原图绘制到缓存图片对象
- 使用绘图工具对象将水印(文字/图片)绘制到缓存图片
- 创建图像编码工具类
- 使用图像编码工具类,输出缓存图像到目标文件
效果图:
上传页:
原图:
单文字水印:
单图片水印:
多文字水印:
多图片水印:
单文字水印开发
所谓但文字水印,就是在一张图片上添加一条文字水印。其中我们主要的流程是通过imageio工具类解码对应的图片,然后创建bufferimage对象,通过bufferimage对象创建graphics2d对象,再通过graphics2d对象绘制原图到bufferimage对象。然后,我们还可以使用graphics2d对象来设置水印的相关信息,如水印内容、字体大小、字体风格等。
这里需要说明的是我们需要计算水印文本的宽度,中文长度即文本宽度,英文长度为文本宽度的二分之一。具体可以参考我源码中的相关内容。
//计算水印文本长度 //1、中文长度即文本长度 2、英文长度为文本长度二分之一 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; }
//添加单条文字水印方法 public string textwatermark(multipartfile myfile,string imagefilename) { inputstream is =null; outputstream os =null; int x = 636; int y = 700; try { //使用imageio解码图片 image image = imageio.read(myfile.getinputstream()); //计算原始图片宽度长度 int width = image.getwidth(null); int height = image.getheight(null); //创建图片缓存对象 bufferedimage bufferedimage = new bufferedimage(width, height, bufferedimage.type_int_rgb); //创建java绘图工具对象 graphics2d graphics2d = bufferedimage.creategraphics(); //参数主要是,原图,坐标,宽高 graphics2d.drawimage(image, 0, 0, width, height, null); graphics2d.setfont(new font(font_name, font_style, font_size)); graphics2d.setcolor(font_color); //使用绘图工具将水印绘制到图片上 //计算文字水印宽高值 int waterwidth = font_size*gettextlength(mark_text); int waterheight = font_size; //计算水印与原图高宽差 int widthdiff = width-waterwidth; int heightdiff = height-waterheight; //水印坐标设置 if (x > widthdiff) { x = widthdiff; } if (y > heightdiff) { y = heightdiff; } //水印透明设置 graphics2d.setcomposite(alphacomposite.getinstance(alphacomposite.src_atop, alpha)); //纵坐标在下方,不增加字体高度会靠上 graphics2d.drawstring(mark_text, x, y+font_size); graphics2d.dispose(); os = new fileoutputstream(upload_path+"/"+imagefilename); //创建图像编码工具类 jpegimageencoder en = jpegcodec.createjpegencoder(os); //使用图像编码工具类,输出缓存图像到目标文件 en.encode(bufferedimage); if(is!=null){ is.close(); } if(os!=null){ os.close(); } } catch (ioexception e) { e.printstacktrace(); } return "success"; }
单图片水印开发
单图片水印和上面单文字的代码流程大致一致,这里只讲解不同之处。
首先我们需要获得水印图片的路径,然后创建水印文件对象,同样通过imageio工具类解码水印图片,中间我们就不需要计算文本长宽了,因为单文字中的长宽即是我们水印图片的长宽。
//水印图片路径 //水印坐标设置 string logopath = "/img/logo.png"; string realpath = request.getsession().getservletcontext().getrealpath(logopath); file logo = new file(realpath); image imagelogo = imageio.read(logo); int widthlogo = imagelogo.getwidth(null); int heightlogo = imagelogo.getheight(null); int widthdiff = width-widthlogo; int heightdiff = height-heightlogo; //水印坐标设置 if (x > widthdiff) { x = widthdiff; } if (y > heightdiff) { y = heightdiff; } //水印透明设置 graphics2d.setcomposite(alphacomposite.getinstance(alphacomposite.src_atop, alpha)); graphics2d.drawimage(imagelogo, x, y, null);
多文字水印开发
其实多文字水印开发和单文字也是类似的,主要的不同点是我们需要将bufferimage对象进行旋转。因为绘制水印并不支持旋转水印绘制,所以我们需要对原图进行旋转绘制,然后通过循环,我们就可以将一个文字水印多次绘制在原图上了。
//旋转原图,注意旋转角度为弧度制。后面两个参数为旋转的坐标中心 graphics2d.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){ graphics2d.drawstring(mark_text, x, y); y+=waterheight+100; } x+=waterwidth+100; }
多图片水印开发
与上文相同,多图片水印需要先读取水印图片,然后对水印设置透明度,在对原图进行旋转,然后通过循环,我们就可以将一个图片水印多次绘制在原图上。
//水印图片路径 string logopath = "/img/logo.png"; string realpath = request.getsession().getservletcontext().getrealpath(logopath); file logo = new file(realpath); image imagelogo = imageio.read(logo); int widthlogo = imagelogo.getwidth(null); int heightlogo = imagelogo.getheight(null); //水印透明设置 graphics2d.setcomposite(alphacomposite.getinstance(alphacomposite.src_atop, alpha)); graphics2d.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){ graphics2d.drawimage(imagelogo, x, y, null); y+=heightlogo+100; } x+=widthlogo+100; }
业务类完整代码:
import java.awt.alphacomposite; import java.awt.color; import java.awt.font; import java.awt.graphics2d; import java.awt.image; import java.awt.image.bufferedimage; import java.io.file; import java.io.fileoutputstream; import java.io.ioexception; import java.io.inputstream; import java.io.outputstream; import javax.imageio.imageio; import javax.servlet.http.httpservletrequest; import org.springframework.stereotype.service; import org.springframework.web.multipart.multipartfile; import com.allan.service.watermarkservice; import com.sun.image.codec.jpeg.jpegcodec; import com.sun.image.codec.jpeg.jpegimageencoder; @service public class watermarkserviceimpl implements watermarkservice{ //定义上传的文件夹 private static final string upload_path = "e:/save"; //定义水印文字样式 private static final string mark_text = "小卖铺的老爷爷"; private static final string font_name = "微软雅黑"; private static final int font_style = font.bold; private static final int font_size = 60; private static final color font_color = color.black; private static final float alpha = 0.3f; //1、上传图片 public string uploadimage(multipartfile myfile,string imagefilename) { inputstream is =null; outputstream os =null; try{ is = myfile.getinputstream(); os = new fileoutputstream(upload_path+"/"+imagefilename); byte[] buffer =new byte[1024]; int len = 0; while ((len=is.read(buffer))>0){ os.write(buffer); } }catch(exception e){ e.printstacktrace(); }finally{ if(is!=null){ try { is.close(); } catch (ioexception e) { e.printstacktrace(); } } if(os!=null){ try { os.close(); } catch (ioexception e2) { e2.printstacktrace(); } } } return "success"; } //添加单条文字水印 public string textwatermark(multipartfile myfile,string imagefilename) { inputstream is =null; outputstream os =null; int x = 636; int y = 700; try { image image = imageio.read(myfile.getinputstream()); //计算原始图片宽度长度 int width = image.getwidth(null); int height = image.getheight(null); //创建图片缓存对象 bufferedimage bufferedimage = new bufferedimage(width, height, bufferedimage.type_int_rgb); //创建java绘图工具对象 graphics2d graphics2d = bufferedimage.creategraphics(); //参数主要是,原图,坐标,宽高 graphics2d.drawimage(image, 0, 0, width, height, null); graphics2d.setfont(new font(font_name, font_style, font_size)); graphics2d.setcolor(font_color); //使用绘图工具将水印绘制到图片上 //计算文字水印宽高值 int waterwidth = font_size*gettextlength(mark_text); int waterheight = font_size; //计算水印与原图高宽差 int widthdiff = width-waterwidth; int heightdiff = height-waterheight; //水印坐标设置 if (x > widthdiff) { x = widthdiff; } if (y > heightdiff) { y = heightdiff; } //水印透明设置 graphics2d.setcomposite(alphacomposite.getinstance(alphacomposite.src_atop, alpha)); graphics2d.drawstring(mark_text, x, y+font_size); graphics2d.dispose(); os = new fileoutputstream(upload_path+"/"+imagefilename); //创建图像编码工具类 jpegimageencoder en = jpegcodec.createjpegencoder(os); //使用图像编码工具类,输出缓存图像到目标文件 en.encode(bufferedimage); if(is!=null){ is.close(); } if(os!=null){ os.close(); } } catch (ioexception e) { e.printstacktrace(); } return "success"; } //添加单图片水印 public string imagewatermark(multipartfile myfile,string imagefilename,httpservletrequest request) { inputstream is =null; outputstream os =null; int x = 636; int y = 763; try { image image = imageio.read(myfile.getinputstream()); //计算原始图片宽度长度 int width = image.getwidth(null); int height = image.getheight(null); //创建图片缓存对象 bufferedimage bufferedimage = new bufferedimage(width, height, bufferedimage.type_int_rgb); //创建java绘图工具对象 graphics2d graphics2d = bufferedimage.creategraphics(); //参数主要是,原图,坐标,宽高 graphics2d.drawimage(image, 0, 0, width, height, null); graphics2d.setfont(new font(font_name, font_style, font_size)); graphics2d.setcolor(font_color); //水印图片路径 string logopath = "/img/logo.png"; string realpath = request.getsession().getservletcontext().getrealpath(logopath); file logo = new file(realpath); image imagelogo = imageio.read(logo); int widthlogo = imagelogo.getwidth(null); int heightlogo = imagelogo.getheight(null); int widthdiff = width-widthlogo; int heightdiff = height-heightlogo; //水印坐标设置 if (x > widthdiff) { x = widthdiff; } if (y > heightdiff) { y = heightdiff; } //水印透明设置 graphics2d.setcomposite(alphacomposite.getinstance(alphacomposite.src_atop, alpha)); graphics2d.drawimage(imagelogo, x, y, null); graphics2d.dispose(); os = new fileoutputstream(upload_path+"/"+imagefilename); //创建图像编码工具类 jpegimageencoder en = jpegcodec.createjpegencoder(os); //使用图像编码工具类,输出缓存图像到目标文件 en.encode(bufferedimage); if(is!=null){ is.close(); } if(os!=null){ os.close(); } } catch (ioexception e) { e.printstacktrace(); } return "success"; } //添加多条文字水印 public string moretextwatermark(multipartfile myfile,string imagefilename) { inputstream is =null; outputstream os =null; int x = 636; int y = 763; try { image image = imageio.read(myfile.getinputstream()); //计算原始图片宽度长度 int width = image.getwidth(null); int height = image.getheight(null); //创建图片缓存对象 bufferedimage bufferedimage = new bufferedimage(width, height, bufferedimage.type_int_rgb); //创建java绘图工具对象 graphics2d graphics2d = bufferedimage.creategraphics(); //参数主要是,原图,坐标,宽高 graphics2d.drawimage(image, 0, 0, width, height, null); graphics2d.setfont(new font(font_name, font_style, font_size)); graphics2d.setcolor(font_color); //使用绘图工具将水印绘制到图片上 //计算文字水印宽高值 int waterwidth = font_size*gettextlength(mark_text); int waterheight = font_size; //水印透明设置 graphics2d.setcomposite(alphacomposite.getinstance(alphacomposite.src_atop, alpha)); graphics2d.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){ graphics2d.drawstring(mark_text, x, y); y+=waterheight+100; } x+=waterwidth+100; } graphics2d.dispose(); os = new fileoutputstream(upload_path+"/"+imagefilename); //创建图像编码工具类 jpegimageencoder en = jpegcodec.createjpegencoder(os); //使用图像编码工具类,输出缓存图像到目标文件 en.encode(bufferedimage); if(is!=null){ is.close(); } if(os!=null){ os.close(); } } catch (ioexception e) { e.printstacktrace(); } return "success"; } //多图片水印 public string moreimagewatermark(multipartfile myfile,string imagefilename,httpservletrequest request) { inputstream is =null; outputstream os =null; int x = 636; int y = 763; try { image image = imageio.read(myfile.getinputstream()); //计算原始图片宽度长度 int width = image.getwidth(null); int height = image.getheight(null); //创建图片缓存对象 bufferedimage bufferedimage = new bufferedimage(width, height, bufferedimage.type_int_rgb); //创建java绘图工具对象 graphics2d graphics2d = bufferedimage.creategraphics(); //参数主要是,原图,坐标,宽高 graphics2d.drawimage(image, 0, 0, width, height, null); graphics2d.setfont(new font(font_name, font_style, font_size)); graphics2d.setcolor(font_color); //水印图片路径 string logopath = "/img/logo.png"; string realpath = request.getsession().getservletcontext().getrealpath(logopath); file logo = new file(realpath); image imagelogo = imageio.read(logo); int widthlogo = imagelogo.getwidth(null); int heightlogo = imagelogo.getheight(null); //水印透明设置 graphics2d.setcomposite(alphacomposite.getinstance(alphacomposite.src_atop, alpha)); graphics2d.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){ graphics2d.drawimage(imagelogo, x, y, null); y+=heightlogo+100; } x+=widthlogo+100; } graphics2d.dispose(); os = new fileoutputstream(upload_path+"/"+imagefilename); //创建图像编码工具类 jpegimageencoder en = jpegcodec.createjpegencoder(os); //使用图像编码工具类,输出缓存图像到目标文件 en.encode(bufferedimage); if(is!=null){ is.close(); } if(os!=null){ os.close(); } } catch (ioexception e) { e.printstacktrace(); } return "success"; } //计算水印文本长度 //1、中文长度即文本长度 2、英文长度为文本长度二分之一 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; } }
最后再说明下,本demo是在上次的文件导入导出的框架基础上编写的,源码中有些其它demo的代码,主要使用的类有watermarkcontroller.java、watermarkservice.java、watermarkserviceimpl.java,因为代码中我是硬编码到e:/save文件夹下的,如果要运行的话,还请先新建此文件夹,或者改为其他文件夹也行。
源码地址:
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。