package com.ycl.task;
|
|
import lombok.RequiredArgsConstructor;
|
import lombok.extern.slf4j.Slf4j;
|
import org.springframework.stereotype.Component;
|
|
import javax.imageio.IIOImage;
|
import javax.imageio.ImageIO;
|
import javax.imageio.ImageWriteParam;
|
import javax.imageio.ImageWriter;
|
import java.awt.Graphics2D;
|
import java.awt.RenderingHints;
|
import java.awt.image.BufferedImage;
|
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayOutputStream;
|
import java.io.File;
|
import java.io.FileInputStream;
|
import java.io.FileOutputStream;
|
import java.io.IOException;
|
import java.io.InputStream;
|
import java.io.OutputStream;
|
import java.time.LocalDate;
|
import java.time.format.DateTimeFormatter;
|
import java.util.Arrays;
|
import java.util.HashSet;
|
import java.util.Iterator;
|
import java.util.Set;
|
|
|
/**
|
* 图片压缩任务(增强版:支持PNG尺寸压缩)
|
* 功能:处理D:/testImage/目录下所有图片,用压缩逻辑压缩后替换原文件
|
* 新增:PNG图片按最大宽度/高度限制进行尺寸压缩
|
* @author : zxl
|
*/
|
@Slf4j
|
@RequiredArgsConstructor
|
@Component("imageCompressTask")
|
public class ImageCompressTask {
|
|
// 本地图片目录(固定为D盘testImage)
|
private static final String BASE_DIRECTORY = "/opt/zgyw/uploadPath/";
|
// 你定义的支持图片格式集合(完全保留)
|
private static final Set<String> IMAGE_FORMATS = new HashSet<>(Arrays.asList(
|
"jpg", "jpeg", "png", "gif", "bmp", "webp", "svg", "tiff"
|
));
|
|
// PNG尺寸压缩配置
|
private static final int PNG_MAX_WIDTH = 1920; // PNG最大宽度
|
private static final int PNG_MAX_HEIGHT = 1080; // PNG最大高度
|
private static final boolean PNG_SCALE_ENABLED = true; // 是否启用PNG尺寸压缩
|
private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
/**
|
* 获取昨天日期的目录路径
|
* @return 完整的目录路径,如:/opt/zgyw/uploadPath/2025-09-21
|
*/
|
private String getYesterdayDirectory() {
|
LocalDate yesterday = LocalDate.now().minusDays(1);
|
String yesterdayStr = yesterday.format(formatter);
|
return BASE_DIRECTORY + yesterdayStr;
|
}
|
/**
|
* 主函数:单函数整合本地目录读写+你的压缩逻辑
|
*/
|
public void processLocalImageDirectory() {
|
String imageDirectory = getYesterdayDirectory();
|
log.info("====== 开始处理本地图片目录:{} ======", imageDirectory);
|
log.info("PNG尺寸压缩配置:最大宽度{}px,最大高度{}px,启用状态:{}",
|
PNG_MAX_WIDTH, PNG_MAX_HEIGHT, PNG_SCALE_ENABLED);
|
|
// 1. 检查目录有效性
|
File imageDir = new File(imageDirectory);
|
if (!imageDir.exists() || !imageDir.isDirectory()) {
|
log.error("目录不存在或无效:{}", imageDirectory);
|
return;
|
}
|
|
// 2. 读取目录下所有文件(排除子目录)
|
File[] files = imageDir.listFiles(file -> !file.isDirectory());
|
if (files == null || files.length == 0) {
|
log.info("目录下无文件可处理");
|
return;
|
}
|
|
// 3. 遍历处理每个本地文件
|
int successCount = 0;
|
int failCount = 0;
|
int skipCount = 0;
|
|
for (File localFile : files) {
|
String fileName = localFile.getName();
|
String formatName = getFileExtension(fileName);
|
|
if (!isImageFormat(formatName)) {
|
log.debug("跳过非图片文件:{}", fileName);
|
skipCount++;
|
continue;
|
}
|
|
try {
|
compressAndReplaceLocalFile(localFile, formatName);
|
successCount++;
|
} catch (Exception e) {
|
log.error("压缩图片 {} 失败", fileName, e);
|
failCount++;
|
}
|
}
|
|
// 输出处理结果
|
log.info("====== 图片处理完成 ======");
|
log.info("统计:成功{}个 | 失败{}个 | 跳过{}个 | 总计{}个",
|
successCount, failCount, skipCount, files.length);
|
}
|
|
/**
|
* 增强版压缩逻辑:支持PNG尺寸压缩 + JPG质量压缩
|
*/
|
private void compressAndReplaceLocalFile(File localFile, String formatName) throws IOException {
|
InputStream inputStream = null;
|
ByteArrayOutputStream outputStream = null;
|
File tempFile = null;
|
|
try {
|
inputStream = new FileInputStream(localFile);
|
long originalSize = localFile.length();
|
|
// ====================== 增强的压缩逻辑 ======================
|
BufferedImage originalImage = ImageIO.read(inputStream);
|
if (originalImage == null) {
|
throw new IOException("无法读取图片文件:" + localFile.getName());
|
}
|
|
outputStream = new ByteArrayOutputStream();
|
|
// PNG特殊处理:尺寸压缩
|
if ("png".equalsIgnoreCase(formatName) && PNG_SCALE_ENABLED) {
|
BufferedImage scaledImage = scalePngImage(originalImage);
|
if (scaledImage != null) {
|
// 使用PNG格式写入缩放后的图片
|
ImageIO.write(scaledImage, "png", outputStream);
|
log.debug("PNG尺寸压缩完成:{}x{} → {}x{}",
|
originalImage.getWidth(), originalImage.getHeight(),
|
scaledImage.getWidth(), scaledImage.getHeight());
|
} else {
|
// 缩放失败,使用原图
|
ImageIO.write(originalImage, "png", outputStream);
|
}
|
}
|
// 其他格式:使用原有的质量压缩逻辑
|
else {
|
Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName(formatName);
|
if (!writers.hasNext()) {
|
throw new IllegalArgumentException("不支持的图片格式: " + formatName);
|
}
|
|
ImageWriter writer = writers.next();
|
ImageWriteParam param = writer.getDefaultWriteParam();
|
|
if (param.canWriteCompressed()) {
|
param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
|
param.setCompressionQuality(0.1F);
|
}
|
|
writer.setOutput(ImageIO.createImageOutputStream(outputStream));
|
writer.write(null, new IIOImage(originalImage, null, null), param);
|
writer.dispose();
|
}
|
|
inputStream.close();
|
inputStream = new ByteArrayInputStream(outputStream.toByteArray());
|
// ====================== 压缩逻辑结束 ======================
|
|
// 写入临时文件并替换原文件
|
tempFile = File.createTempFile("compressed_", "." + formatName);
|
try (OutputStream tempOs = new FileOutputStream(tempFile)) {
|
byte[] buffer = new byte[1024];
|
int len;
|
while ((len = inputStream.read(buffer)) != -1) {
|
tempOs.write(buffer, 0, len);
|
}
|
}
|
|
long compressedSize = tempFile.length();
|
if (compressedSize > 0) {
|
if (compressedSize < originalSize) {
|
if (localFile.delete()) {
|
if (tempFile.renameTo(localFile)) {
|
log.info("压缩成功:{}(原大小:{}KB → 压缩后:{}KB,减少:{}%)",
|
localFile.getName(),
|
originalSize / 1024,
|
compressedSize / 1024,
|
String.format("%.1f", (1 - (double)compressedSize / originalSize) * 100));
|
} else {
|
throw new IOException("替换原文件失败");
|
}
|
} else {
|
throw new IOException("删除原文件失败(可能被占用)");
|
}
|
} else {
|
log.warn("压缩无效(体积未减小):{}(原大小:{}KB,压缩后:{}KB)",
|
localFile.getName(), originalSize / 1024, compressedSize / 1024);
|
// 即使体积未减小也替换,因为可能是PNG尺寸压缩但质量更好的情况
|
if (localFile.delete() && tempFile.renameTo(localFile)) {
|
log.info("图片已优化(质量提升):{}", localFile.getName());
|
}
|
}
|
} else {
|
throw new IOException("压缩后文件为空");
|
}
|
|
} finally {
|
// 清理资源
|
if (inputStream != null) {
|
try { inputStream.close(); } catch (IOException e) { log.error("输入流关闭失败", e); }
|
}
|
if (outputStream != null) {
|
try { outputStream.close(); } catch (IOException e) { log.error("输出流关闭失败", e); }
|
}
|
if (tempFile != null && tempFile.exists() && !tempFile.delete()) {
|
log.warn("临时文件清理失败:{}", tempFile.getAbsolutePath());
|
}
|
}
|
}
|
|
/**
|
* PNG尺寸压缩:按比例缩放图片到最大尺寸限制内
|
*/
|
private BufferedImage scalePngImage(BufferedImage originalImage) {
|
int originalWidth = originalImage.getWidth();
|
int originalHeight = originalImage.getHeight();
|
|
// 检查是否需要缩放
|
if (originalWidth <= PNG_MAX_WIDTH && originalHeight <= PNG_MAX_HEIGHT) {
|
log.debug("PNG图片尺寸未超限,无需缩放:{}x{}", originalWidth, originalHeight);
|
return null;
|
}
|
|
// 计算缩放比例
|
double widthRatio = (double) PNG_MAX_WIDTH / originalWidth;
|
double heightRatio = (double) PNG_MAX_HEIGHT / originalHeight;
|
double ratio = Math.min(widthRatio, heightRatio);
|
|
int newWidth = (int) (originalWidth * ratio);
|
int newHeight = (int) (originalHeight * ratio);
|
|
// 确保尺寸不为0
|
if (newWidth <= 0) newWidth = 1;
|
if (newHeight <= 0) newHeight = 1;
|
|
log.debug("PNG缩放比例:{} → 新尺寸:{}x{}", ratio, newWidth, newHeight);
|
|
// 创建缩放后的图片
|
BufferedImage scaledImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
|
Graphics2D g2d = scaledImage.createGraphics();
|
|
// 设置渲染参数以获得更好的缩放质量
|
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
|
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
|
// 绘制缩放后的图片
|
g2d.drawImage(originalImage, 0, 0, newWidth, newHeight, null);
|
g2d.dispose();
|
|
return scaledImage;
|
}
|
|
/**
|
* 完全保留你的方法:获取文件扩展名
|
*/
|
private static String getFileExtension(String fileName) {
|
if (fileName == null || !fileName.contains(".")) {
|
return "jpg";
|
}
|
return fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();
|
}
|
|
/**
|
* 完全保留你的方法:判断是否为图片格式
|
*/
|
private static boolean isImageFormat(String formatName) {
|
if (formatName == null || formatName.isEmpty()) {
|
return false;
|
}
|
return IMAGE_FORMATS.contains(formatName.toLowerCase());
|
}
|
}
|