package org.sxkj.common.utils;
|
|
|
import javax.imageio.IIOImage;
|
import javax.imageio.ImageIO;
|
import javax.imageio.ImageWriteParam;
|
import javax.imageio.ImageWriter;
|
import javax.imageio.stream.ImageOutputStream;
|
import javax.imageio.stream.MemoryCacheImageOutputStream;
|
import java.awt.*;
|
import java.awt.image.BufferedImage;
|
import java.io.*;
|
import java.nio.file.Files;
|
import java.nio.file.StandardCopyOption;
|
import java.util.Iterator;
|
|
public class ImgZipUtil {
|
|
/**
|
* tif 压缩转换成 jpeg
|
* @param inputFile
|
* @param targetSizeKB 压缩目标大小
|
* @param targetName 压缩目标名称,例如 _small,_show
|
* @return
|
*/
|
public static File compressTifToJpeg(File inputFile,int targetSizeKB,String targetName) throws IOException {
|
File jpeg = TifCompressorUtils.convertTifToJpeg(inputFile, ".jpeg");
|
File file = compressImage(jpeg, targetSizeKB, targetName);
|
return file;
|
}
|
|
/**
|
* 图片压缩
|
* @param inputFile
|
* @param targetSizeKB 压缩目标大小
|
* @param targetName 压缩目标名称,例如 _small,_show
|
* @return
|
* @throws IOException
|
*/
|
public static File compressImage(File inputFile, int targetSizeKB,String targetName) throws IOException {
|
BufferedImage image = ImageIO.read(inputFile);
|
|
// 计算目标图片的尺寸
|
long targetSizeBytes = targetSizeKB * 1024L;
|
long originalSizeBytes = getImageSize(image);
|
double compressionRatio = (double) targetSizeBytes / originalSizeBytes;
|
int targetWidth = (int) (image.getWidth() * Math.sqrt(compressionRatio));
|
int targetHeight = (int) (image.getHeight() * Math.sqrt(compressionRatio));
|
|
// 使用ImageIO进行压缩
|
BufferedImage compressedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
|
Graphics2D graphics = compressedImage.createGraphics();
|
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
graphics.drawImage(image, 0, 0, targetWidth, targetHeight, null);
|
graphics.dispose();
|
|
String name = inputFile.getName();
|
String fileName = name.substring(0,name.lastIndexOf(".")); // 获取文件名称
|
String extension = name.substring(name.lastIndexOf(".")); // 获取后缀名
|
|
// 将压缩后的图片写入输出文件
|
File outputFile = new File(inputFile.getParent(), fileName + targetName + extension);
|
ImageIO.write(compressedImage, "jpeg", outputFile);
|
return outputFile;
|
}
|
/**
|
* 图片压缩
|
*
|
* @return
|
* @throws IOException
|
*/
|
public static File compressImage(File inputFile, int targetSizeKB) throws IOException {
|
BufferedImage image = ImageIO.read(inputFile);
|
|
// 计算目标图片的尺寸
|
long targetSizeBytes = targetSizeKB * 1024L;
|
long originalSizeBytes = getImageSize(image);
|
double compressionRatio = (double) targetSizeBytes / originalSizeBytes;
|
int targetWidth = (int) (image.getWidth() * Math.sqrt(compressionRatio));
|
int targetHeight = (int) (image.getHeight() * Math.sqrt(compressionRatio));
|
|
// 使用ImageIO进行压缩
|
BufferedImage compressedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
|
Graphics2D graphics = compressedImage.createGraphics();
|
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
graphics.drawImage(image, 0, 0, targetWidth, targetHeight, null);
|
graphics.dispose();
|
|
// 将压缩后的图片写入输出文件
|
File outputFile = new File(inputFile.getParent(), "compressed_" + inputFile.getName());
|
ImageIO.write(compressedImage, "jpeg", outputFile);
|
return outputFile;
|
}
|
public static ByteArrayInputStream compressImage(InputStream inputStream, int targetSizeKB) throws IOException {
|
if (!inputStream.markSupported()) {
|
inputStream = new BufferedInputStream(inputStream);
|
}
|
|
String format = getOriginalFormat(inputStream); // format will be "jpg" or "png"
|
|
BufferedImage image = ImageIO.read(inputStream);
|
if (image == null) {
|
throw new IOException("图片读取异常");
|
}
|
|
// 将 "jpeg" 统一为 "jpg" 以便内部处理,虽然 ImageIO 通常两者都认
|
String internalFormat = "jpeg".equalsIgnoreCase(format) ? "jpg" : format.toLowerCase();
|
long originalSizeBytes = getImageSize(image, internalFormat);
|
|
// 如果小于2MB,直接返回原图 (重新编码成原始格式)
|
// if (originalSizeBytes < 2 * 1024 * 1024) {
|
// ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
// if (!ImageIO.write(image, internalFormat, baos)) {
|
// throw new IOException("Failed to write original image to byte array for format: " + internalFormat);
|
// }
|
// return new ByteArrayInputStream(baos.toByteArray());
|
// }
|
|
long targetSizeBytes = targetSizeKB * 1024L;
|
if (targetSizeBytes <= 0) {
|
targetSizeBytes = 1;
|
}
|
if (originalSizeBytes <= 0) {
|
originalSizeBytes = 1;
|
}
|
|
double compressionRatio = (double) targetSizeBytes / originalSizeBytes;
|
// 限制压缩比,避免图片缩得太小或反而变大
|
compressionRatio = Math.min(1.0, Math.max(0.01, compressionRatio)); // 最小缩小到1%
|
|
int targetWidth = (int) (image.getWidth() * Math.sqrt(compressionRatio));
|
int targetHeight = (int) (image.getHeight() * Math.sqrt(compressionRatio));
|
|
targetWidth = Math.max(1, targetWidth);
|
targetHeight = Math.max(1, targetHeight);
|
|
BufferedImage compressedImage;
|
if ("png".equalsIgnoreCase(internalFormat)) {
|
// 对于PNG,保留Alpha通道
|
compressedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_ARGB);
|
} else { // "jpg"
|
// 对于JPG,使用RGB (JPG不支持Alpha)
|
// 如果原始图像有Alpha,转换到RGB时Alpha会丢失
|
// 如果原始PNG image.getType() 是 TYPE_CUSTOM 或其他复杂类型,直接绘制到TYPE_INT_RGB可能需要注意
|
// 但通常 ImageIO.read() 会返回一个标准类型
|
if (image.getType() == BufferedImage.TYPE_INT_ARGB || image.getType() == BufferedImage.TYPE_INT_ARGB_PRE || image.getTransparency() != BufferedImage.OPAQUE) {
|
// 如果原图是PNG且有透明度,但目标是JPG,创建一个RGB图像,绘制时透明部分会变黑(或Graphics2D的默认背景色)
|
// 更好的做法是先在白色背景上绘制原图,再缩放
|
BufferedImage tempImageWithWhiteBg = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB);
|
Graphics2D g2dTemp = tempImageWithWhiteBg.createGraphics();
|
g2dTemp.setColor(java.awt.Color.WHITE); // 设置背景为白色
|
g2dTemp.fillRect(0, 0, image.getWidth(), image.getHeight());
|
g2dTemp.drawImage(image, 0, 0, null);
|
g2dTemp.dispose();
|
image = tempImageWithWhiteBg; // 后续缩放基于这个去除了透明度的图
|
}
|
compressedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
|
}
|
|
Graphics2D graphics = compressedImage.createGraphics();
|
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
// 如果需要更高质量的缩放(但更慢)
|
// graphics.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
// graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
graphics.drawImage(image, 0, 0, targetWidth, targetHeight, null);
|
graphics.dispose();
|
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
// 对于JPG,可以尝试设置压缩质量,但这会使大小更难预测
|
// if ("jpg".equalsIgnoreCase(internalFormat)) {
|
// Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpeg"); // "jpeg" or "jpg"
|
// if (writers.hasNext()) {
|
// ImageWriter writer = writers.next();
|
// ImageWriteParam param = writer.getDefaultWriteParam();
|
// if (param.canWriteCompressed()) {
|
// param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
// param.setCompressionQuality(0.75f); // 0.0 (高压缩) to 1.0 (高质量)
|
// }
|
// ImageOutputStream ios = ImageIO.createImageOutputStream(byteArrayOutputStream);
|
// writer.setOutput(ios);
|
// writer.write(null, new IIOImage(compressedImage, null, null), param);
|
// ios.close();
|
// writer.dispose();
|
// } else {
|
// throw new IOException("No JPEG writer found for setting quality.");
|
// }
|
// } else {
|
if (!ImageIO.write(compressedImage, internalFormat, byteArrayOutputStream)) {
|
throw new IOException("处理缩略图片失败: " + internalFormat);
|
}
|
// }
|
|
return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
|
}
|
|
|
// 获取图像的字节大小
|
|
/**
|
* 图片压缩
|
*
|
* @return
|
* @throws IOException
|
*/
|
public static ByteArrayInputStream compressImageByInputString(InputStream inputStream, int targetSizeKB) throws IOException {
|
BufferedImage image = ImageIO.read(inputStream);
|
// 计算目标图片的尺寸
|
long targetSizeBytes = targetSizeKB * 1024L;
|
long originalSizeBytes = getImageSize(image);
|
double compressionRatio = (double) targetSizeBytes / originalSizeBytes;
|
int targetWidth = (int) (image.getWidth() * Math.sqrt(compressionRatio));
|
int targetHeight = (int) (image.getHeight() * Math.sqrt(compressionRatio));
|
// 使用ImageIO进行压缩
|
BufferedImage compressedImage = new BufferedImage(targetWidth, targetHeight, BufferedImage.TYPE_INT_RGB);
|
Graphics2D graphics = compressedImage.createGraphics();
|
graphics.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
graphics.drawImage(image, 0, 0, targetWidth, targetHeight, null);
|
// 释放图形上下文
|
graphics.dispose();
|
// 将处理后的图片转换为输入流
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
// 将压缩后的图片写入输出文件
|
ImageIO.write(compressedImage, "jpeg",byteArrayOutputStream);
|
// 返回
|
return new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
|
}
|
|
public static long getImageSize(BufferedImage image) {
|
File tempFile;
|
try {
|
tempFile = File.createTempFile("temp", ".tmp");
|
ImageIO.write(image, "jpg", tempFile);
|
long size = tempFile.length();
|
tempFile.delete();
|
return size;
|
} catch (IOException ex) {
|
ex.printStackTrace();
|
return 0;
|
}
|
}
|
public static long getImageSize(BufferedImage image, String format) throws IOException {
|
// 规范化格式名称 (例如,将 "jpeg" 转为 "jpg",全部转为小写)
|
String actualFormat = "jpeg".equalsIgnoreCase(format) ? "jpg" : format.toLowerCase();
|
|
File tempFile = null;
|
try {
|
// 1. 创建一个临时文件来写入图像数据
|
tempFile = File.createTempFile("temp_img_size_", "." + actualFormat);
|
// System.out.println("DEBUG (getImageSize): Temp file will be created at: " + tempFile.getAbsolutePath());
|
|
// 2. 获取指定格式的 ImageWriter
|
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(actualFormat);
|
if (!writers.hasNext()) {
|
// 如果没有找到 ImageWriter,则无法进行写入操作
|
throw new IOException("无法找到合适的 ImageWriter 来写入 " + actualFormat + " 格式");
|
}
|
ImageWriter writer = writers.next(); // 通常使用第一个找到的 writer
|
|
// 3. 创建一个 ImageOutputStream 连接到临时文件
|
ImageOutputStream ios = null;
|
try {
|
ios = ImageIO.createImageOutputStream(tempFile);
|
if (ios == null) {
|
// 这种情况不常见,但以防万一
|
throw new IOException("无法为临时文件创建 ImageOutputStream: " + tempFile.getAbsolutePath());
|
}
|
|
// 4. 将 ImageWriter 的输出目标设置为 ImageOutputStream
|
writer.setOutput(ios);
|
|
// 5. 写入图像数据
|
// 使用 IIOImage 包装 BufferedImage,可以传递缩略图和元数据(这里都设为 null)
|
// 第三个参数是 ImageWriteParam,设为 null 表示使用默认写入参数
|
writer.write(null, new IIOImage(image, null, null), null);
|
|
// 6. 确保所有缓冲数据都已写入流 (可选,通常 close() 会做这个)
|
// ios.flush();
|
// System.out.println("DEBUG (getImageSize): Image written to temp file.");
|
|
} finally {
|
// 7. 关闭 ImageOutputStream (非常重要,确保数据落盘并释放资源)
|
if (ios != null) {
|
try {
|
ios.close();
|
// System.out.println("DEBUG (getImageSize): ImageOutputStream closed.");
|
} catch (IOException e) {
|
// 记录关闭流时的错误,但可能仍需尝试删除文件和释放writer
|
System.err.println("Error closing ImageOutputStream: " + e.getMessage());
|
}
|
}
|
// 8. 释放 ImageWriter 资源
|
if (writer != null) {
|
writer.dispose();
|
}
|
}
|
|
// 9. 获取临时文件的大小
|
long length = tempFile.length();
|
// System.out.println("DEBUG (getImageSize): Temp file length: " + length + " bytes for format " + actualFormat);
|
|
return length;
|
|
} finally {
|
// 10. 无论成功与否,都尝试删除临时文件
|
if (tempFile != null && tempFile.exists()) {
|
// System.out.println("DEBUG (getImageSize): Deleting temp file: " + tempFile.getAbsolutePath());
|
if (!tempFile.delete()) {
|
System.err.println("警告: 无法删除临时文件: " + tempFile.getAbsolutePath());
|
// 可以选择让它在JVM退出时删除
|
// tempFile.deleteOnExit();
|
}
|
}
|
}
|
}
|
|
public static String getOriginalFormat(InputStream inputStream) throws IOException {
|
if (!inputStream.markSupported()) {
|
inputStream = new BufferedInputStream(inputStream);
|
}
|
inputStream.mark(8); // 8 bytes should be enough for JPG and PNG
|
byte[] header = new byte[8];
|
int bytesRead = inputStream.read(header);
|
inputStream.reset();
|
|
if (bytesRead < 4) {
|
System.err.println("Warning: Could not read enough bytes to determine format. Defaulting to jpg.");
|
return "jpg";
|
}
|
|
// JPEG: FF D8 FF
|
if (header[0] == (byte) 0xFF && header[1] == (byte) 0xD8 && header[2] == (byte) 0xFF) {
|
return "jpg"; // 标准的 ImageIO format name 是 "jpeg" 或 "jpg"
|
}
|
// PNG: 89 50 4E 47 (‰PNG)
|
else if (header[0] == (byte) 0x89 && header[1] == 'P' && header[2] == 'N' && header[3] == 'G') {
|
return "png";
|
}
|
|
System.err.println("Warning: Unknown image format based on header (only JPG/PNG supported). Defaulting to jpg.");
|
return "jpg"; // 默认 fallback
|
}
|
|
|
/**
|
* 图片压缩
|
*
|
* @param originalImageFile
|
* @param compressionQuality 图片压缩质量 0-1
|
* @return
|
* @throws IOException
|
*/
|
public static File compressImageAndGetFile(File originalImageFile, float compressionQuality) throws IOException {
|
BufferedImage originalImage = ImageIO.read(originalImageFile);
|
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
ImageWriter writer = ImageIO.getImageWritersByFormatName("jpg").next();
|
try (ImageOutputStream ios = new MemoryCacheImageOutputStream(baos)) {
|
writer.setOutput(ios);
|
ImageWriteParam param = writer.getDefaultWriteParam();
|
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
param.setCompressionQuality(compressionQuality);
|
writer.write(null, new javax.imageio.IIOImage(originalImage, null, null), param);
|
} finally {
|
writer.dispose();
|
}
|
InputStream in = new ByteArrayInputStream(baos.toByteArray());
|
File tempFile = File.createTempFile("compressed-", ".jpg");
|
tempFile.deleteOnExit();
|
Files.copy(in, tempFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
return tempFile;
|
}
|
}
|
|
/**
|
* 图片压缩
|
*
|
* @param inputStream
|
* @param compressionQuality 图片压缩质量 0-1
|
* @return
|
* @throws IOException
|
*/
|
public static ByteArrayInputStream compressImageAndGetByteInputStream(InputStream inputStream, float compressionQuality) throws IOException {
|
BufferedImage originalImage = ImageIO.read(inputStream);
|
ByteArrayInputStream byteArrayInputStream = null;
|
ImageWriter writer = null;
|
ImageOutputStream ios = null;
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
try{
|
writer = ImageIO.getImageWritersByFormatName("jpg").next();
|
ios = new MemoryCacheImageOutputStream(baos);
|
writer.setOutput(ios);
|
ImageWriteParam param = writer.getDefaultWriteParam();
|
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
param.setCompressionQuality(compressionQuality);
|
writer.write(null, new javax.imageio.IIOImage(originalImage, null, null), param);
|
byteArrayInputStream = new ByteArrayInputStream(baos.toByteArray());
|
}catch (Exception e){
|
e.printStackTrace();
|
}finally {
|
byteArrayInputStream.close();
|
baos.close();
|
ios.close();
|
writer.dispose();
|
}
|
return byteArrayInputStream;
|
}
|
|
/**
|
* inputStream 转 byte
|
* @param inputStream
|
* @return
|
* @throws IOException
|
*/
|
public static byte[] readInputStreamToByteArray(InputStream inputStream) throws IOException {
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
byte[] buffer = new byte[1024];
|
int bytesRead;
|
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
byteArrayOutputStream.write(buffer, 0, bytesRead);
|
}
|
return byteArrayOutputStream.toByteArray();
|
}
|
|
|
|
}
|