记录下图片工具类中杀毒、格式校验、压缩相关功能
PS:压缩需要用到thumbnailator,需要引入pom。
>
>net.coobird >
>thumbnailator >
>0.4.20 >
>
package com.dh.demos.web;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.coobird.thumbnailator.Thumbnails;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.MemoryCacheImageInputStream;
import java.awt.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Iterator;
import java.util.List;
/**
* @author : lssffy
* @Description : 图片处理工具类
* @date : 2023/9/5 14:30
*/
@Slf4j
public class ImageUtils {
public static final int DEFAULT_DOUBLE_BYTES = 1024;
public static final List<String> IMG_WHITE_LIST = new ArrayList<String>();
static {
IMG_WHITE_LIST.add("jpg");
IMG_WHITE_LIST.add("jpeg");
}
/**
* @param imageBytes 源图片字节数组
* @param fileSize 指定图片大小,单位kb 限定200-500kb
* @return 压缩质量后的图片字节数组
* @author : lssffy
* @Description : 根据指定大小压缩图片,通过重新调整图片尺寸,可以去除图片文件中包含恶意代码,完成病毒扫描
*/
public static byte[] compressImageForScale(byte[] imageBytes, long fileSize) {
if (imageBytes == null || imageBytes.length <= 0 || imageBytes.length < fileSize * DEFAULT_DOUBLE_BYTES) {
return imageBytes;
}
long size = imageBytes.length;
double accuracy = getAccuracy(fileSize / DEFAULT_DOUBLE_BYTES);
try {
while (imageBytes.length > fileSize * DEFAULT_DOUBLE_BYTES) {
ByteArrayInputStream inputStream = new ByteArrayInputStream(imageBytes);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(imageBytes.length);
Thumbnails.of(inputStream).scale(accuracy).outputQuality(accuracy).toOutputStream(outputStream);
imageBytes = outputStream.toByteArray();
}
log.info("图片压缩前大小=" + size / DEFAULT_DOUBLE_BYTES + "kb,图片压缩后大小=" + imageBytes.length / DEFAULT_DOUBLE_BYTES + "kb");
} catch (Exception e) {
log.error("图片压缩失败,异常:{}", e);
return null;
}
return imageBytes;
}
/**
* @param size 源图片大小
* @return 图片压缩质量比
* @author : lssffy
* @Description : 自动调节精度 (经验数值)
*/
public static double getAccuracy(long size) {
double accuracy;
if ((size < 900)) {
accuracy = 0.85;
} else if (size < 2048) {
accuracy = 0.72;
} else if (size < 3275) {
accuracy = 0.66;
} else {
accuracy = 0.64;
}
return accuracy;
}
/**
* @param imgObject 图片对象
* @param imgBase64Str 图片base64字符串
* @return 返回true 则是jpg jpeg的图片文件
* @author : lssffy
* @Description : 图片防篡改,检测文件的真是格式,限定jpg jpeg,可以过滤掉大部分病毒、木马、WebShell,其他恶意脚本文件
*/
public static boolean preventImageTamper(Object imgObject, String imgBase64Str) {
Decoder decoder = Base64.getDecoder();
byte[] bs = decoder.decode(imgBase64Str);
for (int i = 0; i < bs.length; i++) {
if (bs[i] < 0) {
bs[i] += 256;
}
}
boolean flag = false;
try {
boolean f1 = checkImageFirst(imgObject);
if (f1) {
boolean f2 = checkImageType(bs);
boolean f3 = checkImageRelType(bs);
if (f2 == true && f3 == true) {
flag = true;
}
}
} catch (Exception e) {
log.error("图片格式检查出错,异常:{}", e);
throw new IllegalStateException("图片格式检查出错");
}
return flag;
}
/**
* @param imageObject
* @return
* @author : lssffy
* @Description : 通过文件后缀名判断是否是图片格式
*/
public static boolean checkImageFirst(Object imageObject) {
boolean flag = false;
ImgDataDTO imgDataDTO = (ImgDataDTO) imageObject;
String fileName = imgDataDTO.getImgName();
String fileExtension = "";
fileExtension = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
if (IMG_WHITE_LIST.contains(fileExtension)) {
flag = true;
}
return flag;
}
/**
* @param bytes
* @return
* @throws IOException
* @author : lssffy
* @Description : 通过ImageIO来判断文件是否真正的图片格式
*/
public static boolean checkImageType(byte[] bytes) throws IOException {
Image img = null;
ByteArrayInputStream in = null;
try {
in = new ByteArrayInputStream(bytes);
img = ImageIO.read(in);
if (img == null || img.getWidth(null) <= 0 || img.getHeight(null) <= 0) {
return false;
}
return true;
} catch (Exception e) {
log.error("图片文件检查出错,异常{}", e);
return false;
} finally {
img = null;
if (in != null) {
in.close();
}
}
}
/**
* @param bytes
* @return
* @throws Exception
* @author : lssffy
* @Description : 判断文件是否未经篡改的真是JPEG、jpg图片
*/
public static boolean checkImageRelType(byte[] bytes) throws Exception {
boolean ret = false;
ByteArrayInputStream in = null;
MemoryCacheImageInputStream mcis = null;
try {
in = new ByteArrayInputStream(bytes);
mcis = new MemoryCacheImageInputStream(in);
Iterator<ImageReader> it = ImageIO.getImageReaders(mcis);
if (!it.hasNext()) {
log.error("非图片文件");
throw new IOException("上传的不是真正的图片文件");
}
ImageReader reader = it.next();
String formatName = reader.getFormatName().toLowerCase();
log.info("文件真实格式为:{}", formatName);
if (formatName != null && IMG_WHITE_LIST.contains(formatName)) {
ret = true;
}
} catch (IOException e) {
log.error("图片文件检查出错,异常:{}", e);
} finally {
if (mcis != null) {
mcis.close();
}
}
return ret;
}
/**
* @param imgBase64Str
* @return
* @author : lssffy
* @Description : 转换图片,压缩图片
*/
public static byte[] uploadFileToByte(String imgBase64Str) {
if (ObjectUtils.isEmpty(imgBase64Str)) {
return null;
}
Decoder decoder = Base64.getMimeDecoder();
byte[] bs;
try {
bs = decoder.decode(imgBase64Str);
for (int i = 0; i < bs.length; i++) {
if (bs[i] < 0) {
bs[i] += 256;
}
}
} catch (Exception e) {
log.error("Base64转换图片报错,异常:{}", e);
return null;
}
log.info("图片压缩前大小" + bs.length / DEFAULT_DOUBLE_BYTES + "kb");
bs = compressImageForScale(bs, 500);
log.info("图片压缩后大小" + bs.length / DEFAULT_DOUBLE_BYTES + "kb");
return bs;
}
}
/**
* @author : lssffy
* @Description : 照片数据流
*/
@Data
class ImgDataDTO {
private String imgName;
private String imgDataByte;
}