From b85542765637358cb1473629d0ea767ac076aec3 Mon Sep 17 00:00:00 2001
From: zxl <763096477@qq.com>
Date: 星期二, 23 九月 2025 13:36:26 +0800
Subject: [PATCH] 图片压缩
---
ycl-server/pom.xml | 6 +
ycl-server/src/main/java/com/ycl/task/ImageCompressTask.java | 287 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 292 insertions(+), 1 deletions(-)
diff --git a/ycl-server/pom.xml b/ycl-server/pom.xml
index bbec891..445a723 100644
--- a/ycl-server/pom.xml
+++ b/ycl-server/pom.xml
@@ -30,7 +30,11 @@
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
-
+ <dependency>
+ <groupId>net.coobird</groupId>
+ <artifactId>thumbnailator</artifactId>
+ <version>0.4.14</version>
+ </dependency>
<!-- 浠g爜鐢熸垚-->
<dependency>
diff --git a/ycl-server/src/main/java/com/ycl/task/ImageCompressTask.java b/ycl-server/src/main/java/com/ycl/task/ImageCompressTask.java
new file mode 100644
index 0000000..4090ddc
--- /dev/null
+++ b/ycl-server/src/main/java/com/ycl/task/ImageCompressTask.java
@@ -0,0 +1,287 @@
+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;
+
+
+/**
+ * 鍥剧墖鍘嬬缉浠诲姟锛堝寮虹増锛氭敮鎸丳NG灏哄鍘嬬缉锛�
+ * 鍔熻兘锛氬鐞咲:/testImage/鐩綍涓嬫墍鏈夊浘鐗囷紝鐢ㄥ帇缂╅�昏緫鍘嬬缉鍚庢浛鎹㈠師鏂囦欢
+ * 鏂板锛歅NG鍥剧墖鎸夋渶澶у搴�/楂樺害闄愬埗杩涜灏哄鍘嬬缉
+ * @author : zxl
+ */
+@Slf4j
+@RequiredArgsConstructor
+@Component("imageCompressTask")
+public class ImageCompressTask {
+
+ // 鏈湴鍥剧墖鐩綍锛堝浐瀹氫负D鐩榯estImage锛�
+ 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);
+ }
+
+ /**
+ * 澧炲己鐗堝帇缂╅�昏緫锛氭敮鎸丳NG灏哄鍘嬬缉 + 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());
+ }
+}
\ No newline at end of file
--
Gitblit v1.8.0