图片处理工具类,添加水印,图片缩略图,合法性校验
本文完全借鉴https://blog.csdn.net/qq_22174779/article/details/88199898
package xyz.guqing.imageutil;
import java.awt.AlphaComposite;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Transparency;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import javax.imageio.ImageIO;
import org.apache.commons.lang3.StringUtils;
import commons.CommonUtils;
/**
* @ClassName: ImageUtils
* @Description: 图片校验合法性工具类
* @author: guqing
* @date: 2018年12月31日 下午4:05:37
*
*/
public class ImageUtils {
/**
* 允许上传的图片后缀名
*/
private static final String[] IMAGE_TYPE = new String[]{".bmp", ".jpg", ".jpeg", ".gif", ".png"};
/**
* 按指定高度 等比例缩放图片
* @param imageFile
* @param destinationPath
* @param newWidth 新图的宽度,宽度不建议小于400
* @throws IOException
*/
public static void zoomImageScale(File imageFile, String destinationPath, int newWidth) throws IOException {
if(!imageFile.canRead())
return;
BufferedImage bufferedImage = ImageIO.read(imageFile);
if (null == bufferedImage)
return;
int originalWidth = bufferedImage.getWidth();
int originalHeight = bufferedImage.getHeight();
double scale = (double)originalWidth / (double)newWidth; // 缩放的比例
int newHeight = (int)(originalHeight / scale);
zoomImageUtils(imageFile, destinationPath, bufferedImage, newWidth, newHeight);
}
/**
* 在原图片上添加水印
* @param sourceImage 目标图片路径,如:F:/upload/testImageUtil.jpg
* @param waterImg 水印图片路径,如:F:/upload/testImageUtil.jpg
* @param x 水印图片距离目标图片左侧的偏移量,如果x<0, 则在正中间
* @param y 水印图片距离目标图片上侧的偏移量,如果y<0, 则在正中间
* @param alpha 透明度区间[0,1], 0为完全透明,1为完全不透明
* @throws IOException
*/
public static void addWaterMark(File sourceImage, File waterImg, int x, int y, float alpha) throws IOException {
// 加载目标图片
String ext = sourceImage.getAbsolutePath().substring(sourceImage.getAbsolutePath().lastIndexOf(".") + 1);
Image image = ImageIO.read(sourceImage);
int width = image.getWidth(null);
int height = image.getHeight(null);
// 将目标图片加载到内存。
BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g = bufferedImage.createGraphics();
g.drawImage(image, 0, 0, width, height, null);
// 加载水印图片。
Image waterImage = ImageIO.read(waterImg);
int width_1 = waterImage.getWidth(null);
int height_1 = waterImage.getHeight(null);
// 设置水印图片的透明度。
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
// 设置水印图片的位置。
int widthDiff = width - width_1;
int heightDiff = height - height_1;
if (x < 0) {
x = widthDiff / 2;
} else if (x > widthDiff) {
x = widthDiff;
}
if (y < 0) {
y = heightDiff / 2;
} else if (y > heightDiff) {
y = heightDiff;
}
// 将水印图片“画”在原有的图片的制定位置。
g.drawImage(waterImage, x, y, width_1, height_1, null);
// 关闭画笔。
g.dispose();
// 保存目标图片。
ImageIO.write(bufferedImage, ext, sourceImage);
}
/**
* @Description: 将文件经hash打散后保存到picSourcePath路径
* @param: @param sourceFile 源文件流对象
* @param: @param destinationPath 目的存储路径,不需要带文件名,如:F:/upload/
* @param: @return
* @param: @throws IOException
* @return: File
* @throws
*/
public static File writeFilewWithHashBreak (File sourceFile,String destinationPathWithoutName) throws IOException {
//拿到图片原始文件名称
if(sourceFile == null){
throw new RuntimeException("the file is null");
}
String originalFilename = sourceFile.getName();
if(originalFilename == null || originalFilename.length()==0){
return null;
}
//新的图片文件名称:uuid+原图片名称,直接使用uuid+后缀,有中文前端会乱码
String newFileName = CommonUtils.uuid() + originalFilename.substring(originalFilename.lastIndexOf("."));//originalFilename;
//使用hash打散目录
String savePath = makeHashPath(originalFilename, destinationPathWithoutName);
//新图片,hash打散的存储路径+文件名称
File file = new File(savePath + File.separator + newFileName);
//将内存中的数据写入磁盘
transferTo(sourceFile,file);
//上传成功返回文件名
return file;
}
/**
* @Description: 校验上传的图片文件是否合法
* @param: @param filePathWithName 将上传文件存储到磁盘后的绝对路径,如:F:/upload/test.jpg
* @param: @return 如果校验通过返回true,否则返回false
* @param: @throws Exception
* @return: boolean
* @throws
*/
public static boolean isLegal(File filePathWithName) throws Exception{
//先先查文件后缀名是否合法
boolean checkSuffixFlag = checkSuffix(filePathWithName.getAbsolutePath());
if(!checkSuffixFlag){
//如果不合法删除本地文件
deleteFile(filePathWithName.getAbsolutePath());
return false;
}
//检查,参数:File imageFile
boolean isImageFlag = isImageViaWithAndHeight(filePathWithName);
if(!isImageFlag){
//如果不合法删除本地文件
deleteFile(filePathWithName.getAbsolutePath());
return false;
}
return true;
}
/**
* @Description: 将文件保存到磁盘
* @param: @param sourcefile 源文件,如:F:/upload/test1.jpg
* @param: @param destPath 目的文件地址,如:F:/upload/test2.jpg
* @return: void
* @throws
*/
public static void transferTo(File sourcefile, File destPath) {
BufferedInputStream bufferedInputStream = null;
BufferedOutputStream bufferedOutputStream = null;
try {
bufferedInputStream = new BufferedInputStream(new FileInputStream(sourcefile));
bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(destPath));
byte[] buff = new byte[1024];
int i = 0;
while ((i = bufferedInputStream.read(buff)) != -1) {
bufferedOutputStream.write(buff, 0, i);
bufferedOutputStream.flush();//手动刷新该流的缓冲,立即将他们写入预期目标
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally {
try {
if (bufferedInputStream != null) {
bufferedInputStream.close();
}
if (bufferedOutputStream != null) {
bufferedOutputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* @Description: 根据文件绝对路径删除文件
* @param: @param absolutePath 文件的绝对路径
* @param: @return 无返回值
* @throws
*/
private static void deleteFile(String absolutePath) {
File file = new File(absolutePath);
// 如果文件路径所对应的文件存在,并且是一个文件,则直接删除
if(file.exists() && file.isFile()) {
if (!file.delete()) {
throw new RuntimeException("文件删除失败,路径:"+absolutePath);
}
}else {
throw new RuntimeException("文件不存在,路径:"+absolutePath);
}
}
/**
* @Description: 为防止一个目录下面出现太多文件,要使用hash算法打散存储
* @param: @param filename 文件名,要根据文件名生成存储目录
* @param: @param savePath 文件存储路径
* @param: @return 新的存储目录
* @return: String
* @throws
*/
private static String makeHashPath(String filename, String savePath) {
// 得到文件名的hashCode的值,得到的就是filename这个字符串对象在内存中的地址
int hashcode = filename.hashCode();
int dir1 = hashcode & 0xf; // 0--15
int dir2 = (hashcode & 0xf0) >> 4; // 0-15
// 构造新的保存目录,如upload\2\3,upload\3\5,使用当前系统的分割符,这样在不同的系统中就不会出问题
String dir = savePath + File.separator + dir1 + File.separator + dir2;
// File既可以代表文件也可以代表目录
File file = new File(dir);
// 如果目录不存在
if (!file.exists()) {
// 创建目录
file.mkdirs();
}
return dir;
}
/**
* @Description: 校验文件的后缀名是否合法
* @param: @param filename 文件名
* @param: @return 如果上传的文件名以IMAGE_TYPE结尾则返回true,否则返回false
* @return: boolean
* @throws
*/
private static boolean checkSuffix(String filename){
for(String type : IMAGE_TYPE) {
if(StringUtils.endsWithIgnoreCase(filename, type)) {//忽略大小写
//如果上传的文件名以IMAGE_TYPE结尾则返回true
return true;
}
}
//否则返回false
return false;
}
/**
* 通过读取文件并获取其width及height的方式
* 来判断判断当前文件是否图片,这是一种非常简单的方式。
* @param imageFile
* @return 返回true则通过校验,否则不通过
*/
private static boolean isImageViaWithAndHeight(File imageFile) {
if (!imageFile.exists()) {
return false;
}
Image img = null;
try {
img = ImageIO.read(imageFile);
if (img == null || img.getWidth(null) <= 0 || img.getHeight(null) <= 0) {
return false;
}
return true;
} catch (Exception e) {
return false;
} finally {
img = null;
}
}
/**
* @Description: 等比例缩放图片工具类
* @param: @param imageFile 源
* @param: @param newPath 目的
* @param: @param bufferedImage 图片缓冲流
* @param: @param width 图片宽
* @param: @param height 图片的高
* @param: @throws IOException
* @return: void
* @throws
*/
private static void zoomImageUtils(File imageFile, String newPath, BufferedImage bufferedImage, int width, int height)
throws IOException{
String suffix = StringUtils.substringAfterLast(imageFile.getName(), ".");
// 处理 png 背景变黑的问题
if(suffix != null && (suffix.trim().toLowerCase().endsWith("png") || suffix.trim().toLowerCase().endsWith("gif"))){
BufferedImage to= new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = to.createGraphics();
to = g2d.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
g2d.dispose();
g2d = to.createGraphics();
Image from = bufferedImage.getScaledInstance(width, height, Image.SCALE_AREA_AVERAGING);
g2d.drawImage(from, 0, 0, null);
g2d.dispose();
ImageIO.write(to, suffix, new File(newPath));
}else{
BufferedImage newImage = new BufferedImage(width, height, bufferedImage.getType());
Graphics g = newImage.getGraphics();
g.drawImage(bufferedImage, 0, 0, width, height, null);
g.dispose();
ImageIO.write(newImage, suffix, new File(newPath));
}
}
}
使用示例:
public class TestImageUtils {
@Test
public void test1() throws Exception{
//File file = new File("F:/upload/testImageUtil.jpg");
File file = new File("F:/upload/test3.jpg");
File file2 = new File("F:/upload/无标题.png");
//校验图片合法性,如果不合法会删除本地文件
boolean isLegal = ImageUtils.isLegal(file);
System.out.println(isLegal);
//给图片添加水印,-1,-1表示水印居中对齐,0表示水印完全不透明,1表示完全透明
//ImageUtils.addWaterMark(file, file2, -1, -1, 0.5f);
//等比例缩放图片,实现图片缩略图,宽度不建议小于400
ImageUtils.zoomImageScale(file, "F:/upload/test.jpg", 480);
//将文件写到磁盘
ImageUtils.transferTo(file, new File("F:/upload/test.jpg"));
//将文件流经hash打散后写到磁盘
ImageUtils.writeFilewWithHashBreak(file, "F:/upload/");
}
}
工具类依赖:
commons-lang3-3.7.jar
CommonUtils.jar