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