由于是前后端交互,就先给大家介绍一下这个模块的设计思路。
首先前台使用uploader让用户选择图片上传并回显(可以参考我的《uploader.js上传与回显》),而我们实现的这个加水印的功能需要在回显前完成,并把水印后的图片存储在某个位置,假设我们先存储在本地磁盘。
思路:简单理解一下这个过程
1. 后台正确接收到了multipartFile图片对象
2. 获取一系列路径用于读取和存储,如原图绝对路径,水印标识的存储位置,原图加水印后的存储路径
3. 通过ImageIO将原图加载成BufferedImage对象
4. 通过ImageHelper和Graphics在原图某个区域添加水印
5.返回一个8位RGB颜色分量的图像对象(已添加水印)
6. ImageIO把这个对象写入到磁盘中
业务代码部分:
public Map uploadSYImage(HttpServletRequest request,MultipartFile imageFile, String oldsaveName) {
//oldsaveName是原图源文件名不带扩展名
String imgCDNURL = "";
Map map = new HashMap();
try {
//原图绝对路径
String realPath = request.getSession().getServletContext().getRealPath("/");
//相对路径,保存结果图
String resourcePath = "uploadtemp/images/";
//传入的图片不能为空
if (imageFile != null) {
//扩展名
String suffix = "";
//媒体类型必须是二进制的,这种类型规定一次只能提交一个文件,且必须是流或字节数组的形式,多用于传图片
if (imageFile.getContentType().equals("application/octet-stream")) {
//原图的源文件名
String name = imageFile.getOriginalFilename();
if (null != name) {
//从源文件名中截取扩展名
Integer num = name.indexOf(".");
suffix = name.substring(num, name.length());
}
}
//判断上传的图片格式是否合规,这个视情况而定,不需要可以不加,文末有FileUploadUtil的代码可供参考
if (FileUploadUtil.allowUpload(imageFile.getContentType()) || FileUploadUtil.allowSuffix(suffix)) {
//给结果图取名
String fileName = FileUploadUtil.rename(imageFile.getOriginalFilename());
int end = fileName.lastIndexOf(".");
//saveName会作为结果图存储路径的一部分
String saveName = fileName.substring(0, end);
//创建文件
File dir = new File(realPath + resourcePath);
if (!dir.exists()) {
dir.mkdirs();
}
//returnFile为用户上传的原图文件
File returnFile = new File(dir, oldsaveName + "_src.jpg");
imageFile.transferTo(returnFile);
//将原图存入到map中
map.put("file", returnFile);
//图片加水印,把未加水印的换成结果图,名称不变
//获取水印logo图,稍后会把这张水印logo图绘入到原图上
String syUrl = realPath + "res/images/waterImg.png";
//把logo图加载成对象
BufferedImage logoImage = ImageIO.read(new File(syUrl));
//加载原图
BufferedImage pressTo = ImageIO.read(new File(returnFile.getPath()));
//ImageHelper在原图右下角添加水印logo
BufferedImage targetImg = ImageHelper.pressOnRightBottom(pressTo, logoImage);
//给结果图创建路径
String srcImagePath = realPath + resourcePath + saveName;
File file = new File(srcImagePath + "_src.jpg");
file.createNewFile();
//根据targetImg的长宽返回一个具有合成整数像素的8位RGB颜色分量的图像,意思就是把原来的targetImg像素调成8位的,方便存储和后期滤镜处理(降低清晰度)
BufferedImage rgbImage = new BufferedImage(targetImg.getWidth(), targetImg.getHeight(),
BufferedImage.TYPE_INT_RGB);
rgbImage.createGraphics().drawImage(targetImg, 0, 0, null, null);
//生成结果图文件
ImageIO.write(rgbImage, "jpg", new File(srcImagePath + "_src.jpg"));
log.info("uploadHeadImage upload to cdn start! ");
//上传图片
imgCDNURL = imageUpload(file);
map.put("waterImg", imgCDNURL);
log.info("uploadHeadImage upload to cdn end! ");
}
}
} catch (Exception e) {
log.error(e.getMessage(), e);
}
return map;
}
自定义FileUploadUtil工具类,验证用户上传图片的格式是否合规
public class FileUploadUtil {
public static final List ALLOW_TYPES = Arrays.asList(
"image/jpg","image/jpeg","image/png","image/gif","image/x-png","image/pjpeg"
);
public static final List ALLOW_SUFFIX = Arrays.asList(
".jpg",".jpeg",".png",".gif",".x-png",".pjpeg"
);
//文件重命名
public static String rename(String fileName){
int i = fileName.lastIndexOf(".");
String str = fileName.substring(i);
return new Date().getTime()+""+ new Random().nextInt(99999999) +str;
}
//校验文件类型是否是被允许的
public static boolean allowUpload(String postfix){
return ALLOW_TYPES.contains(postfix);
}
//校验文件后缀是否是被允许的
public static boolean allowSuffix(String suffix){
return ALLOW_SUFFIX.contains(suffix);
}
}
ImageHelper:通过Graphics类实现在原图上添加水印logo
import java.awt.Graphics;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.HeadlessException;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.Transparency;
import java.awt.geom.AffineTransform;
import java.awt.image.AffineTransformOp;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Iterator;
import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.swing.ImageIcon;
public class ImageHelper {
/*
* 根据尺寸图片居中裁剪
*/
public static void cutCenterImage(String src,String dest,int w,int h) throws IOException{
Iterator> iterator = ImageIO.getImageReadersByFormatName("jpg");
ImageReader reader = (ImageReader)iterator.next();
InputStream in=new FileInputStream(src);
ImageInputStream iis = ImageIO.createImageInputStream(in);
reader.setInput(iis, true);
ImageReadParam param = reader.getDefaultReadParam();
int imageIndex = 0;
Rectangle rect = new Rectangle((reader.getWidth(imageIndex)-w)/2, (reader.getHeight(imageIndex)-h)/2, w, h);
param.setSourceRegion(rect);
BufferedImage bi = reader.read(0,param);
ImageIO.write(bi, "jpg", new File(dest));
}
/*
* 图片裁剪二分之一
*/
public static void cutHalfImage(String src,String dest) throws IOException{
Iterator> iterator = ImageIO.getImageReadersByFormatName("jpg"); //getImageReadersByFormatName("jpg")
ImageReader reader = (ImageReader)iterator.next();
InputStream in=new FileInputStream(src);
ImageInputStream iis = ImageIO.createImageInputStream(in);
reader.setInput(iis, true);
ImageReadParam param = reader.getDefaultReadParam();
int imageIndex = 0;
int width = reader.getWidth(imageIndex)/2;
int height = reader.getHeight(imageIndex)/2;
Rectangle rect = new Rectangle(width/2, height/2, width, height);
param.setSourceRegion(rect);
BufferedImage bi = reader.read(0,param);
ImageIO.write(bi, "jpg", new File(dest));
}
/*
* 图片裁剪
*/
public static BufferedImage cutImage(InputStream target,int x,int y,int w,int h) throws IOException{
Iterator iterator = ImageIO.getImageReadersByFormatName("jpg");//getImageReadersByFormatName("jpg")
ImageReader reader = iterator.next();
ImageInputStream iis = ImageIO.createImageInputStream(target);
reader.setInput(iis, true);
ImageReadParam param = reader.getDefaultReadParam();
Rectangle rect = new Rectangle(x,y,w,h);
param.setSourceRegion(rect);
BufferedImage bi = reader.read(0,param);
return bi;
}
/*
* 图片缩放320*320 240*192 560*400 120*96
*/
public static void zoomImage(String src,String dest,int w,int h) throws Exception {
double wr=0,hr=0;
File srcFile = new File(src);
File destFile = new File(dest);
BufferedImage bufImg = ImageIO.read(srcFile);
Image itemp = bufImg.getScaledInstance(w, h, Image.SCALE_SMOOTH);
wr=w*1.0/bufImg.getWidth();
hr=h*1.0/bufImg.getHeight();
AffineTransformOp ato = new AffineTransformOp(AffineTransform.getScaleInstance(wr, hr), null);
itemp = ato.filter(bufImg, null);
try {
ImageIO.write((BufferedImage) itemp,dest.substring(dest.lastIndexOf(".")+1), destFile);
} catch (Exception ex) {
ex.printStackTrace();
}
}
/*
* 图片缩放
*/
public static BufferedImage zoomImage(BufferedImage bufImg,int w,int h){
double wr=0,hr=0;
bufImg.getScaledInstance(w, h, Image.SCALE_SMOOTH);
wr=w*1.0/bufImg.getWidth();
hr=h*1.0/bufImg.getHeight();
AffineTransformOp ato = new AffineTransformOp(AffineTransform.getScaleInstance(wr, hr), null);
bufImg = ato.filter(bufImg, null);
return bufImg;
}
public static void pressImage(String pressImg1,String logo, String targetImg,
int x, int y) {
try {
File file1 = new File(pressImg1);
// BufferedImage image = pressImage(ImageIO.read(file1),ImageIO.read(new File(logo)),x,y);
BufferedImage image = pressOnRightBottom(ImageIO.read(file1),ImageIO.read(new File(logo)));
ImageIO.write(image,file1.getName().replaceAll("^[^\\.]+\\.", "") ,new File(targetImg)) ;//ImageIO.write(image, "jpg",new File(targetImg)) ;
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 把图片印刷到图片上
*
* @param targetImg
* -- 水印文件
* @param logoImg
* -- 目标文件
* @param x
* --x坐标
* @param y
* --y坐标
* @return
* @return
*/
public final static BufferedImage pressImage(BufferedImage pressImg1,BufferedImage pressImg2,
int x, int y) {
try {
//目标文件
int wideth = pressImg1.getWidth();
int height = pressImg1.getHeight();
Graphics g =pressImg1.getGraphics();
if(g==null){
pressImg1.createGraphics();
}else{
g.drawImage(pressImg1, 0, 0, wideth, height, null);
}
//水印文件
int wideth_biao = pressImg2.getWidth(null);//获得水印图真实宽度,无图返回-1
int height_biao = pressImg2.getHeight(null);//获得水印图真实高度,无图返回-1
x=x>0?x:(x+wideth-wideth_biao);
y=y>0?y:(y+height-height_biao);
g.drawImage(pressImg2, x, y, wideth_biao, height_biao, null);
//水印文件结束
g.dispose();
} catch (Exception e) {
e.printStackTrace();
}
return pressImg1;
}
public final static BufferedImage pressOnRightBottom(BufferedImage pressTo,BufferedImage logoImage){
//目标文件
int wideth = pressTo.getWidth();
int height = pressTo.getHeight();
//水印文件
int wideth_biao = logoImage.getWidth();
int height_biao = logoImage.getHeight();
return pressImage(pressTo,logoImage,(int)((wideth-wideth_biao)*0.95),(int)((height-height_biao)*0.95));
}
public static void main(String[] args) throws Exception {
//覆盖到目标上
pressImage("d:/0001.jpg","d:/0002.jpg","d:/0003.jpg",
328,82);
}
public static BufferedImage toBufferedImage(Image image) {
if (image instanceof BufferedImage) {
return (BufferedImage)image;
}
// This code ensures that all the pixels in the image are loaded
image = new ImageIcon(image).getImage();
// Determine if the image has transparent pixels; for this method's
// implementation, see e661 Determining If an Image Has Transparent Pixels
//boolean hasAlpha = hasAlpha(image);
// Create a buffered image with a format that's compatible with the screen
BufferedImage bimage = null;
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
try {
int transparency = Transparency.OPAQUE;
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
bimage = gc.createCompatibleImage(
image.getWidth(null), image.getHeight(null), transparency);
} catch (HeadlessException e) {
}
if (bimage == null) {
int type = BufferedImage.TYPE_INT_RGB;
bimage = new BufferedImage(image.getWidth(null), image.getHeight(null), type);
}
Graphics g = bimage.createGraphics();
g.drawImage(image, 0, 0, null);
g.dispose();
return bimage;
}
}