日常开发中,会遇到图片处理,比如图片缩略图生成,图片压缩等,以下工具类提供图片压缩功能。
package com.zx.test.common.utils;
import com.drew.imaging.ImageMetadataReader;
import com.drew.metadata.Directory;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifIFD0Directory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Decoder;
import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.ImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Iterator;
/**
* 图像处理工具类
*
* @author
* Create on 2023/9/11
*/
@Slf4j
public class ImageThumbnailUtils {
/**
* 用于生成缩略图以及压缩图片
*
* @param quality 压缩质量 范围0-1,1为最高质量
* @param inputStream 图片输入流
* @return
* @throws IOException
*/
public static byte[] createThumbnail(float quality, InputStream inputStream) throws IOException {
log.info("生成缩略图------");
ByteArrayOutputStream baos = cloneInputStream(inputStream);
// 打开两个新的输入流
InputStream streamOne = new ByteArrayInputStream(baos.toByteArray());
InputStream streamTwo = new ByteArrayInputStream(baos.toByteArray());
BufferedImage originalImage = ImageIO.read(streamOne);
int height = originalImage.getHeight();
int width = originalImage.getWidth();
if (width > 1500) {
// 定义新画布宽高
height = (int) (height * quality);
width = (int) (width * quality);
}
// 创建一个新的缩放后的图像 必须,不然png转jpg时会有背景失真
Image tmpImage = originalImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
BufferedImage thumbnail = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 在缩放后的图像上绘制原始图像
thumbnail.getGraphics().drawImage(tmpImage, 0, 0, null);
// 压缩 使用jpg格式压缩
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
ImageIO.write(thumbnail, "jpg", byteArrayOutputStream);
byte[] byteArray = byteArrayOutputStream.toByteArray();
byteArrayOutputStream.close();
return byteArray;
}
/**
* 用于ocr识别图片规格压缩
*
* @param quality 设置的压缩质量 0-1
* @param fileBase64 图片输入流
* @param minLength 最短边最小长度 px
* @param maxLength 最长边最长长度 px
* @param size 要求的大小 kb
* @return
* @throws IOException
*/
public static byte[] createThumbnailCheck(MultipartFile file, float quality, String fileBase64, int minLength, int maxLength, int size) throws IOException {
if (file == null) {
String base64 = BASE64DecodedMultipartFile.resolveMimeType(fileBase64);
file = BASE64DecodedMultipartFile.base64ToMultipart(base64);
}
log.info("COR识别前处理图片-------------");
long fileSize = file.getSize() / 1024;
ByteArrayOutputStream baos = cloneInputStream(file.getInputStream());
// 打开两个新的输入流
InputStream streamOne = new ByteArrayInputStream(baos.toByteArray());
InputStream streamTwo = new ByteArrayInputStream(baos.toByteArray());
BufferedImage originalImage = ImageIO.read(streamOne);
//double compressionRatio = (double) size / fileSize;
//原图height width
int height = originalImage.getHeight();
int width = originalImage.getWidth();
log.info("图片原像素,height:{},width:{}", originalImage.getHeight(), originalImage.getWidth());
// if (compressionRatio < 1) {
// width = (int) (originalImage.getWidth() * Math.sqrt(compressionRatio));
// height = (int) (originalImage.getHeight() * Math.sqrt(compressionRatio));
// }
// 判断原图宽高是否在最长最短范围内
// 找到原图宽高的最长最短边,判断是否在范围内,,不是的话进行调整
// if (height >= width) {
// float scale = (float) maxLength * 0.5f / height;
// // 当最长边大于maxLength 设置最长边为maxLength width按比例缩小
// if (height > maxLength) {
// width = (int) (width * scale);
// height = (int) (maxLength * 0.5);
// }
// if (width < minLength) {
// width = minLength;
// }
// } else {
// float scale = (float) maxLength * 0.5f / width;
// if (width > maxLength) {
// height = (int) (height * scale);
// width = (int) (maxLength * 0.5);
// }
// if (height < minLength) {
// height = minLength;
// }
// }
float scale = (float) 828 / width;
height = (int) (height * scale);
width = 828;
log.info("图片后像素,height:{},width:{}", height, width);
// 创建一个新的缩放后的图像
//Image tmpImage = originalImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
BufferedImage thumbnail = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
// 在缩放后的图像上绘制原始图像
thumbnail.getGraphics().drawImage(originalImage, 0, 0, width, height, null);
// 压缩 使用jpg格式压缩
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
// 判断当前图片是否需要压缩
ImageIO.write(thumbnail, "JPEG", byteArrayOutputStream);
float size1 = (float) byteArrayOutputStream.size() / 1024;
if (size1 < size) {
byteArrayOutputStream.close();
return byteArrayOutputStream.toByteArray();
} else {
byteArrayOutputStream.reset();
}
// 定义循环 按压缩比例循环,每次压缩按照设定的压缩比例循环
float qualityRes = 1f;
while (true) {
qualityRes = quality * qualityRes;
byteArrayOutputStream = createThumbnailOnly(thumbnail, qualityRes, byteArrayOutputStream);
float size2 = (float) byteArrayOutputStream.size() / 1024;
if (size2 <= size) {
break;
}
byteArrayOutputStream.reset();
}
byte[] byteArray = byteArrayOutputStream.toByteArray();
log.info("图片进行压缩,压缩前size:{}", file.getSize());
byteArrayOutputStream.close();
return byteArray;
}
/**
* 用于ocr识别图片压缩前判别
*
* @param file 文件 (为null时传fileBase64)
* @param fileBase64 base64
* @param minLength 最短边最小长度 px
* @param maxLength 最长边最长长度 px
* @param size 要求的大小 kb
* @return
* @throws IOException
*/
public static boolean checkImageByBase64(MultipartFile file, String fileBase64, int minLength, int maxLength, int size) throws IOException {
if (file == null) {
String base64 = BASE64DecodedMultipartFile.resolveMimeType(fileBase64);
file = BASE64DecodedMultipartFile.base64ToMultipart(base64);
}
BufferedImage originalImage = ImageIO.read(file.getInputStream());
// 判断是否需要压缩
// 判断最小长度,最大长度,图片大小
float fileSize = (float) file.getSize() / 1024;
if (fileSize > size) {
log.info("图片大小超过4M");
return true;
}
int width = originalImage.getWidth();
int height = originalImage.getHeight();
if (width > maxLength || width < minLength || height > maxLength || height < minLength) {
log.info("图片像素不符合要求");
return true;
}
return false;
}
public static ByteArrayOutputStream createThumbnailOnly(BufferedImage thumbnail, float quality, ByteArrayOutputStream byteArrayOutputStream) throws IOException {
ImageOutputStream imageOutputStream = ImageIO.createImageOutputStream(byteArrayOutputStream);
ImageWriter writer = ImageIO.getImageWritersByFormatName("JPEG").next();
writer.setOutput(imageOutputStream);
// 设置压缩参数
ImageWriteParam defaultWriteParam = writer.getDefaultWriteParam();
if (defaultWriteParam.canWriteCompressed()) {
// 指定压缩方式为MODE_EXPLICIT
defaultWriteParam.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
// 压缩程度,参数quality是取值0~1范围内
defaultWriteParam.setCompressionQuality(quality);
}
writer.write(null, new IIOImage(thumbnail, null, null), defaultWriteParam);
// 统一转换为jpg格式
BufferedImage image = ImageIO.read(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
ImageIO.write(image, "JPEG", byteArrayOutputStream);
// 确保所有更改都已写入 ImageOutputStream 中
imageOutputStream.flush();
// 关闭 ImageOutputStream
imageOutputStream.close();
byteArrayOutputStream.flush();
return byteArrayOutputStream;
}
private static ByteArrayOutputStream cloneInputStream(InputStream input) {
try {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = input.read(buffer)) > -1) {
baos.write(buffer, 0, len);
}
baos.flush();
return baos;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}