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