From 7c0603315e57e3765270a8ac6b310b5a32af5a40 Mon Sep 17 00:00:00 2001
From: zqy <2522236926@qq.com>
Date: 星期五, 02 一月 2026 15:11:13 +0800
Subject: [PATCH] 新增压缩图 和 视频封面 都返回base64

---
 zhang-content/src/main/java/com/ruoyi/service/impl/VideoProcessService.java |  888 +++++++++++++++++++++++++++++++++++++++++-----------------
 1 files changed, 625 insertions(+), 263 deletions(-)

diff --git a/zhang-content/src/main/java/com/ruoyi/service/impl/VideoProcessService.java b/zhang-content/src/main/java/com/ruoyi/service/impl/VideoProcessService.java
index 6229599..1b60a01 100644
--- a/zhang-content/src/main/java/com/ruoyi/service/impl/VideoProcessService.java
+++ b/zhang-content/src/main/java/com/ruoyi/service/impl/VideoProcessService.java
@@ -1,6 +1,7 @@
 package com.ruoyi.service.impl;
 
 import com.ruoyi.common.config.RuoYiConfig;
+import com.ruoyi.common.utils.StringUtils;
 import lombok.extern.slf4j.Slf4j;
 import lombok.Data;
 import org.springframework.beans.factory.annotation.Value;
@@ -8,13 +9,20 @@
 import org.springframework.web.multipart.MultipartFile;
 import ws.schild.jave.*;
 
-
+import javax.imageio.IIOImage;
+import javax.imageio.ImageIO;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
+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.Path;
-import java.nio.file.Paths;
+
 import java.util.*;
-import java.util.concurrent.CompletableFuture;
+import java.util.Base64;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 @Service
 @Slf4j
@@ -22,9 +30,6 @@
 
     @Value("${video.upload-dir:./uploads/videos}")
     private String uploadDir;
-
-    @Value("${video.thumbnail-dir:/profile/thumbnails}")
-    private String thumbnailDir;
 
     @Value("${video.thumbnail.width:320}")
     private int thumbnailWidth;
@@ -38,64 +43,575 @@
     @Value("${video.thumbnail.format:jpg}")
     private String thumbnailFormat;
 
-    @Value("${video.thumbnail.count:1}")
-    private int thumbnailCount; // 鐢熸垚灏侀潰鏁伴噺
-
     // 鏀寔鐨勮棰戞牸寮�
     private static final Set<String> SUPPORTED_VIDEO_FORMATS =
         new HashSet<>(Arrays.asList("mp4", "avi", "mov", "mkv", "flv", "wmv", "webm", "mpeg", "mpg", "3gp", "m4v"));
 
+
+
+    public File convertToFile(MultipartFile multipartFile) throws IOException {
+        String originalFilename = multipartFile.getOriginalFilename();
+        String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
+        File tempFile = File.createTempFile("video_", extension);
+        multipartFile.transferTo(tempFile);
+        tempFile.deleteOnExit();
+        return tempFile;
+    }
+
+
+
+
+
+
+
     /**
-     * 澶勭悊瑙嗛鏂囦欢锛氬彧鐢熸垚灏侀潰
+     * 澶勭悊鍥剧墖鏂囦欢
      */
-    public VideoProcessResult processVideo(MultipartFile file) {
-        VideoProcessResult result = new VideoProcessResult();
+    public Map<String, Object> processImage(File file, int width, int height,
+                                            float quality, String format,boolean deleteY) throws IOException {
+        Map<String, Object> result = new HashMap<>();
+
+        // 璇诲彇鍥剧墖
+        BufferedImage image = ImageIO.read(file);
+        if (image == null) {
+            result.put("success", false);
+            result.put("message", "鏃犳硶璇诲彇鍥剧墖鏂囦欢");
+            return result;
+        }
+
+        int originalWidth = image.getWidth();
+        int originalHeight = image.getHeight();
+
+        // 璋冩暣灏哄
+        if (width > 0 && height > 0) {
+            image = resizeImage(image, width, height, true);
+        }
+
+        // 鍘嬬缉骞惰浆鎹负Base64
+        byte[] compressedBytes = compressImage(image, quality, format);
+        String base64 = Base64.getEncoder().encodeToString(compressedBytes);
+        String dataUrl = "data:image/" + format + ";base64," + base64;
+
+        // 杩斿洖缁撴灉
+        result.put("success", true);
+        result.put("type", "image");
+        result.put("format", format);
+        result.put("compressedSize", compressedBytes.length);
+        result.put("compressedWidth", width > 0 ? width : originalWidth);
+        result.put("compressedHeight", height > 0 ? height : originalHeight);
+        result.put("base64", dataUrl);
+        result.put("message", "鍥剧墖鍘嬬缉鎴愬姛");
+        if (deleteY) {
+            file.delete();
+            result.put("originalDeleted", false);
+            result.put("message", "鍥剧墖鍘嬬缉鎴愬姛");
+        }
+        return result;
+    }
+
+
+
+    /**
+     * 璋冩暣鍥剧墖澶у皬
+     */
+    private BufferedImage resizeImage(BufferedImage originalImage, int targetWidth,
+                                      int targetHeight, boolean keepAspectRatio) {
+        int originalWidth = originalImage.getWidth();
+        int originalHeight = originalImage.getHeight();
+
+        // 濡傛灉瀹介珮閮戒负0锛屽垯涓嶈皟鏁�
+        if (targetWidth <= 0 && targetHeight <= 0) {
+            return originalImage;
+        }
+
+        int newWidth, newHeight;
+
+        if (keepAspectRatio) {
+            // 淇濇寔瀹介珮姣�
+            if (targetWidth <= 0) {
+                // 鍙粰楂樺害
+                double scale = (double) targetHeight / originalHeight;
+                newWidth = (int) (originalWidth * scale);
+                newHeight = targetHeight;
+            } else if (targetHeight <= 0) {
+                // 鍙粰瀹藉害
+                double scale = (double) targetWidth / originalWidth;
+                newWidth = targetWidth;
+                newHeight = (int) (originalHeight * scale);
+            } else {
+                // 涓や釜缁村害閮界粰浜嗭紝淇濇寔瀹介珮姣旂缉鏀�
+                double widthRatio = (double) targetWidth / originalWidth;
+                double heightRatio = (double) targetHeight / originalHeight;
+                double scale = Math.min(widthRatio, heightRatio);
+                newWidth = (int) (originalWidth * scale);
+                newHeight = (int) (originalHeight * scale);
+            }
+        } else {
+            // 涓嶄繚鎸佸楂樻瘮
+            newWidth = targetWidth > 0 ? targetWidth : originalWidth;
+            newHeight = targetHeight > 0 ? targetHeight : originalHeight;
+        }
+
+        // 纭繚鏈�灏忎负1
+        newWidth = Math.max(1, newWidth);
+        newHeight = Math.max(1, newHeight);
+
+        BufferedImage resizedImage = new BufferedImage(newWidth, newHeight,
+            originalImage.getType() == 0 ? BufferedImage.TYPE_INT_ARGB : originalImage.getType());
+
+        Graphics2D g = resizedImage.createGraphics();
+        g.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
+            RenderingHints.VALUE_INTERPOLATION_BILINEAR);
+        g.setRenderingHint(RenderingHints.KEY_RENDERING,
+            RenderingHints.VALUE_RENDER_QUALITY);
+        g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
+            RenderingHints.VALUE_ANTIALIAS_ON);
+
+        g.drawImage(originalImage, 0, 0, newWidth, newHeight, null);
+        g.dispose();
+
+        return resizedImage;
+    }
+
+    /**
+     * 鍘嬬缉鍥剧墖
+     */
+    private byte[] compressImage(BufferedImage image, float quality, String format) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+
+        // 鏍规嵁鏍煎紡閫夋嫨鍘嬬缉鏂瑰紡
+        if ("jpg".equalsIgnoreCase(format) || "jpeg".equalsIgnoreCase(format)) {
+            compressAsJpeg(image, baos, quality);
+        } else if ("png".equalsIgnoreCase(format)) {
+            compressAsPng(image, baos, 6); // PNG鍘嬬缉绾у埆6
+        } else {
+            // 鍏朵粬鏍煎紡浣跨敤榛樿鏂瑰紡
+            ImageIO.write(image, format, baos);
+        }
+
+        return baos.toByteArray();
+    }
+
+    /**
+     * 鍘嬬缉涓篔PEG鏍煎紡
+     */
+    private void compressAsJpeg(BufferedImage image, ByteArrayOutputStream output, float quality)
+        throws IOException {
+        Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("jpeg");
+        if (!writers.hasNext()) {
+            ImageIO.write(image, "jpg", output);
+            return;
+        }
+
+        ImageWriter writer = writers.next();
+        ImageWriteParam param = writer.getDefaultWriteParam();
+
+        // 璁剧疆鍘嬬缉璐ㄩ噺
+        if (quality > 0) {
+            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+            param.setCompressionQuality(Math.max(0.1f, Math.min(1.0f, quality)));
+        }
+
+        writer.setOutput(new MemoryCacheImageOutputStream(output));
+        writer.write(null, new IIOImage(image, null, null), param);
+        writer.dispose();
+    }
+
+    /**
+     * 鍘嬬缉涓篜NG鏍煎紡
+     */
+    private void compressAsPng(BufferedImage image, ByteArrayOutputStream output, int compressionLevel)
+        throws IOException {
+        Iterator<ImageWriter> writers = ImageIO.getImageWritersByFormatName("png");
+        if (!writers.hasNext()) {
+            ImageIO.write(image, "png", output);
+            return;
+        }
+
+        ImageWriter writer = writers.next();
+        ImageWriteParam param = writer.getDefaultWriteParam();
+
+        // PNG鐨勫帇缂╃骇鍒紙0-9锛�0鏈�蹇絾鍘嬬缉鐜囦綆锛�9鏈�鎱絾鍘嬬缉鐜囬珮锛�
+        if (compressionLevel >= 0) {
+            param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
+            param.setCompressionType("Deflate");
+            param.setCompressionQuality(Math.max(0.0f, Math.min(1.0f, compressionLevel / 9.0f)));
+        }
+
+        writer.setOutput(new MemoryCacheImageOutputStream(output));
+        writer.write(null, new IIOImage(image, null, null), param);
+        writer.dispose();
+    }
+
+
+
+
+
+
+
+
+
+
+
+
+
+    /**
+     * 澶勭悊瑙嗛鏂囦欢锛氱敓鎴怋ase64缂栫爜鐨勫皝闈�
+     */
+    public Map<String, Object> processVideo(File file, int width, int height,
+                                            float quality, String format,boolean deleteY) {
+        Map<String, Object> result = new HashMap<>();
+        File tempVideoFile = file;
+
+
+        System.out.println();
 
         try {
             // 1. 楠岃瘉瑙嗛鏍煎紡
             if (!isVideoFile(file)) {
-                result.setSuccess(false);
-                result.setMessage("涓嶆敮鎸佺殑瑙嗛鏍煎紡");
+                result.put("success", false);
+                result.put("message", "涓嶆敮鎸佺殑瑙嗛鏍煎紡");
                 return result;
             }
 
-            // 2. 鐢熸垚鍞竴鏂囦欢鍚�
-            String originalFilename = file.getOriginalFilename();
-            String fileExtension = getFileExtension(originalFilename);
-            String uuid = UUID.randomUUID().toString().replace("-", "");
-            String fileName = uuid + fileExtension;
+            // 2. 淇濆瓨鍘熷鏂囦欢鍒颁复鏃剁洰褰�
+//            String originalFilename = file.getName();
+//            tempVideoFile = File.createTempFile("video_", ".temp");
 
-            // 3. 鍒涘缓鐩綍
-            createDirectories();
+            // 3. 鑾峰彇瑙嗛淇℃伅
+//            MultimediaInfo info = getVideoInfo(tempVideoFile);
+//            double durationSeconds = info.getDuration() / 1000.0;
+//            String videoResolution = info.getVideo().getSize().getWidth() + "x" + info.getVideo().getSize().getHeight();
 
-            // 4. 淇濆瓨鍘熷鏂囦欢
-            Path originalPath = Paths.get(uploadDir, fileName);
-            file.transferTo(originalPath.toFile());
+            // 4. 鐢熸垚Base64鏍煎紡鐨勫皝闈�
+            String base64Thumbnail = generateBase64ThumbnailFromFile(
+                tempVideoFile,
+                width > 0 ? width : thumbnailWidth,
+                height > 0 ? height : thumbnailHeight,
+                (int) quality,
+                format != null ? format : thumbnailFormat
+            );
 
-            // 5. 鑾峰彇瑙嗛淇℃伅
-            MultimediaInfo info = getVideoInfo(originalPath.toFile());
+            if (base64Thumbnail == null || base64Thumbnail.isEmpty()) {
+                result.put("success", false);
+                result.put("message", "灏侀潰鐢熸垚澶辫触");
+                return result;
+            }
 
-            // 璁剧疆缁撴灉
-            result.setSuccess(true);
-            result.setMessage("灏侀潰鐢熸垚鎴愬姛");
-            result.setOriginalPath(originalPath.toString());
-            result.setOriginalFilename(originalFilename);
-            result.setFileSize(file.getSize());
-            result.setDuration(info.getDuration() / 1000.0); // 杞崲涓虹
-            result.setResolution(info.getVideo().getSize().getWidth() + "x" + info.getVideo().getSize().getHeight());
-            result.setVideoFormat(info.getFormat());
+            // 5. 鑾峰彇缂╃暐鍥惧ぇ灏�
+            String thumbnailSize = extractImageSizeFromBase64(base64Thumbnail);
+            String[] sizeParts = thumbnailSize.split("x");
+            int thumbWidth = sizeParts.length > 0 ? Integer.parseInt(sizeParts[0]) : 0;
+            int thumbHeight = sizeParts.length > 1 ? Integer.parseInt(sizeParts[1]) : 0;
 
-            // 6. 鐢熸垚灏侀潰
-            String thumbnailPath = generateThumbnail(originalPath.toFile().getPath());
-            result.setThumbnailPath(thumbnailPath);
+            // 6. 杩斿洖缁撴灉
+            result.put("success", true);
+            result.put("type", "video");
+            result.put("format", format != null ? format : thumbnailFormat);
+//            result.put("originalFilename", originalFilename);
+//            result.put("originalWidth", info.getVideo().getSize().getWidth());
+//            result.put("originalHeight", info.getVideo().getSize().getHeight());
+//            result.put("originalResolution", videoResolution);
+//            result.put("duration", durationSeconds);
+//            result.put("videoFormat", info.getFormat());
+//            result.put("compressedWidth", thumbWidth);
+//            result.put("compressedHeight", thumbHeight);
+            result.put("base64", base64Thumbnail);
+            result.put("message", "瑙嗛灏侀潰鐢熸垚鎴愬姛");
 
         } catch (Exception e) {
             log.error("瑙嗛灏侀潰鐢熸垚澶辫触", e);
-            result.setSuccess(false);
-            result.setMessage("灏侀潰鐢熸垚澶辫触: " + e.getMessage());
+            result.put("success", false);
+            result.put("message", "灏侀潰鐢熸垚澶辫触: " + e.getMessage());
+        } finally {
+            // 娓呯悊涓存椂鏂囦欢
+            if (tempVideoFile != null && tempVideoFile.exists() && deleteY) {
+                try {
+                    tempVideoFile.delete();
+                } catch (Exception e) {
+                    log.warn("鍒犻櫎涓存椂瑙嗛鏂囦欢鏃跺嚭閿�", e);
+                }
+            }
         }
 
         return result;
+    }
+    /**
+     * 鐢熸垚Base64鏍煎紡鐨勫皝闈紙鍙嚜瀹氫箟鍙傛暟锛�
+     */
+    private String generateBase64ThumbnailFromFile(File videoFile, int width, int height,
+                                                   int quality, String format) {
+        File tempThumbnailFile = null;
+        Process process = null;
+
+        try {
+            if (!videoFile.exists()) {
+                log.error("瑙嗛鏂囦欢涓嶅瓨鍦�: {}", videoFile.getAbsolutePath());
+                return null;
+            }
+
+            // 1. 鍒涘缓涓存椂鏂囦欢鐢ㄤ簬淇濆瓨缂╃暐鍥�
+            String ext = format != null ? format : thumbnailFormat;
+            tempThumbnailFile = File.createTempFile("thumb_", "." + ext);
+            tempThumbnailFile.deleteOnExit();
+
+            // 2. 鑾峰彇瑙嗛鏃堕暱锛岃绠楁埅鍥炬椂闂寸偣
+            MultimediaInfo info = getVideoInfo(videoFile);
+            double durationSeconds = info.getDuration() / 1000.0;
+            double screenshotTime = calculateScreenshotTime(durationSeconds);
+
+            // 3. 鏋勫缓FFmpeg鍛戒护
+            String ffmpegCmd = String.format(
+                "ffmpeg -y -i \"%s\" -ss %.2f -vframes 1 -q:v %d -s %dx%d -f image2 \"%s\"",
+                videoFile.getAbsolutePath(),
+                screenshotTime,
+                quality,
+                width,
+                height,
+                tempThumbnailFile.getAbsolutePath()
+            );
+
+            // 4. 鎵цFFmpeg鍛戒护
+            ProcessBuilder processBuilder = new ProcessBuilder();
+            String[] command = isWindows() ?
+                new String[]{"cmd.exe", "/c", ffmpegCmd} :
+                new String[]{"bash", "-c", ffmpegCmd};
+
+            processBuilder.command(command);
+            processBuilder.redirectErrorStream(true);
+            process = processBuilder.start();
+
+            // 5. 绛夊緟鍛戒护瀹屾垚
+            boolean finished = process.waitFor(30, TimeUnit.SECONDS);
+            if (!finished) {
+                process.destroyForcibly();
+                return null;
+            }
+
+            int exitCode = process.waitFor();
+            if (exitCode != 0 || !tempThumbnailFile.exists() || tempThumbnailFile.length() == 0) {
+                return null;
+            }
+
+            // 6. 璇诲彇鍥剧墖鏂囦欢骞惰浆鎹负Base64
+            byte[] fileContent = Files.readAllBytes(tempThumbnailFile.toPath());
+
+            if (!isValidImage(fileContent)) {
+                return null;
+            }
+
+            // 7. 杞崲涓築ase64
+            String base64 = Base64.getEncoder().encodeToString(fileContent);
+            String mimeType = getMimeType(ext);
+
+            return "data:" + mimeType + ";base64," + base64;
+
+        } catch (Exception e) {
+            log.error("鐢熸垚Base64灏侀潰澶辫触", e);
+            return null;
+        } finally {
+            // 娓呯悊璧勬簮
+            if (process != null && process.isAlive()) {
+                process.destroy();
+            }
+
+            if (tempThumbnailFile != null && tempThumbnailFile.exists()) {
+                try {
+                    tempThumbnailFile.delete();
+                } catch (Exception e) {
+                    // ignore
+                }
+            }
+        }
+    }
+
+
+    /**
+     * 鐢熸垚Base64鏍煎紡鐨勫皝闈紙涓嶅瓨鍌ㄦ枃浠讹級
+     */
+    public String generateBase64ThumbnailFromFile(String videoPath) {
+        File videoFile = new File(videoPath);
+        if (!videoFile.exists()) {
+            log.error("瑙嗛鏂囦欢涓嶅瓨鍦�: {}", videoPath);
+            return null;
+        }
+
+        return generateBase64ThumbnailFromFile(videoFile);
+    }
+
+    /**
+     * 鐢熸垚Base64鏍煎紡鐨勫皝闈紙涓嶅瓨鍌ㄦ枃浠讹級
+     */
+    public String generateBase64ThumbnailFromFile(File videoFile) {
+        File tempThumbnailFile = null;
+        Process process = null;
+
+        try {
+            if (!videoFile.exists()) {
+                log.error("瑙嗛鏂囦欢涓嶅瓨鍦�: {}", videoFile.getAbsolutePath());
+                return null;
+            }
+
+            // 1. 鍒涘缓涓存椂鏂囦欢鐢ㄤ簬淇濆瓨缂╃暐鍥�
+            tempThumbnailFile = File.createTempFile("thumb_", "." + thumbnailFormat);
+            tempThumbnailFile.deleteOnExit(); // 纭繚鏂囦欢浼氳鍒犻櫎
+
+            // 2. 鑾峰彇瑙嗛鏃堕暱锛岃绠楁埅鍥炬椂闂寸偣
+            MultimediaInfo info = getVideoInfo(videoFile);
+            double durationSeconds = info.getDuration() / 1000.0;
+            double screenshotTime = calculateScreenshotTime(durationSeconds);
+
+            log.debug("瑙嗛鎴浘淇℃伅 - 鏂囦欢鍚�: {}, 鏃堕暱: {}s, 鎴浘鏃堕棿鐐�: {}s, 鐩爣灏哄: {}x{}, 璐ㄩ噺: {}",
+                videoFile.getName(), durationSeconds, screenshotTime,
+                thumbnailWidth, thumbnailHeight, thumbnailQuality);
+
+            // 3. 鏋勫缓FFmpeg鍛戒护
+            String ffmpegCmd = String.format(
+                "ffmpeg -y -i \"%s\" -ss %.2f -vframes 1 -q:v %d -s %dx%d -f image2 \"%s\"",
+                videoFile.getAbsolutePath(),  // 杈撳叆鏂囦欢
+                screenshotTime,                // 璺宠浆鍒扮殑鏃堕棿鐐癸紙绉掞級
+                thumbnailQuality,              // 杈撳嚭璐ㄩ噺 (2-31锛屽�艰秺灏忚川閲忚秺楂�)
+                thumbnailWidth,                // 杈撳嚭瀹藉害
+                thumbnailHeight,               // 杈撳嚭楂樺害
+                tempThumbnailFile.getAbsolutePath() // 杈撳嚭鏂囦欢
+            );
+
+            log.debug("鎵цFFmpeg鍛戒护: {}", ffmpegCmd);
+
+            // 4. 鎵цFFmpeg鍛戒护
+            ProcessBuilder processBuilder = new ProcessBuilder();
+            String[] command = isWindows() ?
+                new String[]{"cmd.exe", "/c", ffmpegCmd} :
+                new String[]{"bash", "-c", ffmpegCmd};
+
+            processBuilder.command(command);
+            processBuilder.redirectErrorStream(true);
+            process = processBuilder.start();
+
+            // 5. 璇诲彇杈撳嚭锛堢敤浜庤皟璇曪級
+            StringBuilder output = new StringBuilder();
+            try (BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
+                String line;
+                while ((line = reader.readLine()) != null) {
+                    output.append(line).append("\n");
+                }
+            }
+
+            // 6. 绛夊緟鍛戒护瀹屾垚锛堝甫瓒呮椂锛�
+            boolean finished = process.waitFor(30, TimeUnit.SECONDS);
+            if (!finished) {
+                process.destroyForcibly();
+                log.error("FFmpeg鍛戒护鎵ц瓒呮椂锛岃棰戞枃浠�: {}", videoFile.getName());
+                return null;
+            }
+
+            int exitCode = process.waitFor();
+            if (exitCode != 0) {
+                log.error("FFmpeg鍛戒护鎵ц澶辫触锛岄��鍑虹爜: {}, 瑙嗛鏂囦欢: {}, 杈撳嚭: {}",
+                    exitCode, videoFile.getName(), output.toString());
+                return null;
+            }
+
+            // 7. 妫�鏌ョ敓鎴愮殑鍥剧墖鏂囦欢
+            if (!tempThumbnailFile.exists() || tempThumbnailFile.length() == 0) {
+                log.error("鐢熸垚鐨勭缉鐣ュ浘涓虹┖鎴栦笉瀛樺湪锛岃棰戞枃浠�: {}", videoFile.getName());
+                return null;
+            }
+
+            // 8. 璇诲彇鍥剧墖鏂囦欢骞惰浆鎹负Base64
+            byte[] fileContent = Files.readAllBytes(tempThumbnailFile.toPath());
+
+            // 9. 楠岃瘉鍥剧墖鏈夋晥鎬�
+            if (!isValidImage(fileContent)) {
+                log.error("鐢熸垚鐨勭缉鐣ュ浘涓嶆槸鏈夋晥鐨勫浘鐗囷紝瑙嗛鏂囦欢: {}", videoFile.getName());
+                return null;
+            }
+
+            // 10. 杞崲涓築ase64
+            String base64 = Base64.getEncoder().encodeToString(fileContent);
+            String mimeType = getMimeType(thumbnailFormat);
+
+            return "data:" + mimeType + ";base64," + base64;
+
+        } catch (Exception e) {
+            log.error("鐢熸垚Base64灏侀潰澶辫触锛岃棰戞枃浠�: {}", videoFile.getName(), e);
+            return null;
+        } finally {
+            // 娓呯悊璧勬簮
+            if (process != null && process.isAlive()) {
+                process.destroy();
+            }
+
+            // 鍒犻櫎涓存椂鏂囦欢
+            if (tempThumbnailFile != null && tempThumbnailFile.exists()) {
+                try {
+                    boolean deleted = tempThumbnailFile.delete();
+                    if (deleted) {
+                        log.debug("宸插垹闄や复鏃剁缉鐣ュ浘鏂囦欢: {}", tempThumbnailFile.getAbsolutePath());
+                    } else {
+                        log.warn("鏃犳硶鍒犻櫎涓存椂缂╃暐鍥炬枃浠�: {}", tempThumbnailFile.getAbsolutePath());
+                    }
+                } catch (Exception e) {
+                    log.warn("鍒犻櫎涓存椂缂╃暐鍥炬枃浠舵椂鍑洪敊", e);
+                }
+            }
+        }
+    }
+
+    /**
+     * 浠嶣ase64瀛楃涓蹭腑鎻愬彇鍥剧墖灏哄
+     */
+    private String extractImageSizeFromBase64(String base64) {
+        try {
+            // Base64瀛楃涓叉牸寮�: data:image/jpeg;base64,/9j/4AAQSkZJRgABA...
+            String base64Data = base64.split(",")[1];
+            byte[] imageBytes = Base64.getDecoder().decode(base64Data);
+            try (ByteArrayInputStream bis = new ByteArrayInputStream(imageBytes)) {
+                BufferedImage image = ImageIO.read(bis);
+                if (image != null) {
+                    return image.getWidth() + "x" + image.getHeight();
+                }
+            }
+        } catch (Exception e) {
+            log.warn("鏃犳硶浠嶣ase64涓彁鍙栧浘鐗囧昂瀵�", e);
+        }
+        return thumbnailWidth + "x" + thumbnailHeight; // 杩斿洖鐩爣灏哄
+    }
+
+    /**
+     * 璁$畻鎴浘鏃堕棿鐐�
+     */
+    private double calculateScreenshotTime(double durationSeconds) {
+        if (durationSeconds <= 0) {
+            return 0;
+        }
+
+        // 濡傛灉瑙嗛闀垮害灏忎簬1绉掞紝鍦ㄤ腑闂存埅鍥�
+        if (durationSeconds < 1) {
+            return durationSeconds / 2;
+        }
+
+        // 濡傛灉瑙嗛闀垮害灏忎簬3绉掞紝鍦ㄧ1绉掓埅鍥�
+        if (durationSeconds < 3) {
+            return 1.0;
+        }
+
+        // 瑙嗛杈冮暱鏃讹紝鍦�10%鐨勪綅缃埅鍥撅紙浣嗕笉瓒呰繃30绉掞級
+        double time = durationSeconds * 0.1;
+        return Math.min(time, 30.0);
+    }
+
+    /**
+     * 鑾峰彇瑙嗛淇℃伅
+     */
+    private MultimediaInfo getVideoInfo(File videoFile) throws Exception {
+        try {
+            MultimediaObject multimediaObject = new MultimediaObject(videoFile);
+            return multimediaObject.getInfo();
+        } catch (Exception e) {
+            log.error("鑾峰彇瑙嗛淇℃伅澶辫触: {}", videoFile.getAbsolutePath(), e);
+            throw new Exception("鏃犳硶鑾峰彇瑙嗛淇℃伅: " + e.getMessage());
+        }
     }
 
     /**
@@ -129,194 +645,46 @@
     }
 
     /**
-     * 鐢熸垚灏侀潰锛堢缉鐣ュ浘锛�
+     * 鎵归噺鐢熸垚Base64鏍煎紡鐨勭缉鐣ュ浘
      */
-    public String generateThumbnail(String videoPath) throws Exception {
-        File videoFile = new File(RuoYiConfig.getProfile() + videoPath.replace("/profile", ""));
-        if (!videoFile.exists()) {
-            throw new FileNotFoundException("瑙嗛鏂囦欢涓嶅瓨鍦�: " + videoPath);
-        }
-
-        // 鐢熸垚鍞竴鐨勭缉鐣ュ浘鏂囦欢鍚嶏紙涓庤棰戝悓鐩綍锛�
-        String originalName = videoFile.getName();
-        String baseName = getFileNameWithoutExtension(originalName);
-        String thumbnailName = "thumb_" + baseName + "_" +
-            UUID.randomUUID().toString().substring(0, 8) +
-            "." + thumbnailFormat;
-
-        // *** 鍏抽敭淇敼锛氱缉鐣ュ浘鐢熸垚鍦ㄨ棰戞枃浠舵墍鍦ㄧ洰褰� ***
-        // 鑾峰彇瑙嗛鏂囦欢鎵�鍦ㄧ殑鐩綍
-        File videoDir = videoFile.getParentFile();
-        if (videoDir == null) {
-            throw new RuntimeException("鏃犳硶鑾峰彇瑙嗛鏂囦欢鎵�鍦ㄧ洰褰�: " + videoFile.getAbsolutePath());
-        }
-
-        // 纭繚瑙嗛鐩綍瀛樺湪
-        if (!videoDir.exists()) {
-            videoDir.mkdirs();
-        }
-
-        // 鍒涘缓缂╃暐鍥炬枃浠跺璞★紙鍦ㄨ棰戝悓鐩綍锛�
-        File thumbnailFile = new File(videoDir, thumbnailName);
-
-        System.out.println("------------------------缂╃暐鍥炬枃浠跺悕: " + thumbnailName);
-        System.out.println("------------------------缂╃暐鍥惧畬鏁磋矾寰�: " + thumbnailFile.getAbsolutePath());
-        System.out.println("------------------------瑙嗛鏂囦欢鐩綍: " + videoDir.getAbsolutePath());
-
-        // 1. 鑾峰彇瑙嗛鏃堕暱锛岃绠楁埅鍥炬椂闂寸偣
-        MultimediaInfo info = getVideoInfo(videoFile);
-        double durationSeconds = info.getDuration() / 1000.0;
-        double screenshotTime = calculateScreenshotTime(durationSeconds);
-        System.out.println("------------------------鎴浘鏃堕棿鐐�: " + screenshotTime);
-
-        // 2. 鏋勫缓骞舵墽琛� FFmpeg 鍛戒护
-        String ffmpegCmd = String.format(
-            "ffmpeg -i \"%s\" -ss %.2f -vframes 1 -q:v %d -s %dx%d \"%s\"",
-            videoFile.getAbsolutePath(),  // 杈撳叆鏂囦欢
-            screenshotTime,               // 璺宠浆鍒扮殑鏃堕棿鐐癸紙绉掞級
-            thumbnailQuality,             // 杈撳嚭璐ㄩ噺 (2-31锛屽�艰秺灏忚川閲忚秺楂�)
-            thumbnailWidth,               // 杈撳嚭瀹藉害
-            thumbnailHeight,              // 杈撳嚭楂樺害
-            thumbnailFile.getAbsolutePath() // 杈撳嚭鏂囦欢锛堜笌瑙嗛鍚岀洰褰曪級
-        );
-
-
-        Process process = Runtime.getRuntime().exec(ffmpegCmd);
-
-        // 3. 璇诲彇骞舵墦鍗板懡浠よ緭鍑猴紙鐢ㄤ簬璋冭瘯锛�
-        try (BufferedReader errorReader = new BufferedReader(new InputStreamReader(process.getErrorStream()));
-             BufferedReader inputReader = new BufferedReader(new InputStreamReader(process.getInputStream()))) {
-
-            String line;
-            while ((line = errorReader.readLine()) != null) {
-                System.out.println("[FFmpeg Error] " + line);
-            }
-            while ((line = inputReader.readLine()) != null) {
-                System.out.println("[FFmpeg Output] " + line);
-            }
-        }
-
-        int exitCode = process.waitFor();
-        System.out.println("------------------------FFmpeg 閫�鍑虹爜: " + exitCode);
-
-        if (exitCode != 0) {
-            throw new RuntimeException("FFmpeg 鎴浘澶辫触锛岄��鍑虹爜: " + exitCode);
-        }
-
-        if (!thumbnailFile.exists() || thumbnailFile.length() == 0) {
-            throw new RuntimeException("鐢熸垚鐨勭缉鐣ュ浘涓虹┖鎴栦笉瀛樺湪");
-        }
-
-        // 鑾峰彇缂╃暐鍥剧殑缁濆璺緞
-        String absolutePath = thumbnailFile.getAbsolutePath();
-
-        // 灏嗙粷瀵硅矾寰勮浆鎹负鐩稿profilePath鐨勭浉瀵硅矾寰�
-        String relativePath = absolutePath.substring(RuoYiConfig.getProfile().length()).replace("\\", "/");
-
-        // 纭繚鐩稿璺緞浠� / 寮�澶�
-        if (!relativePath.startsWith("/")) {
-            relativePath = "/" + relativePath;
-        }
-
-        System.out.println("------------------------鐢熸垚鐨勭浉瀵硅矾寰�: " + relativePath);
-
-        return "/profile"+relativePath;
-    }
-
-    /**
-     * 璁$畻鎴浘鏃堕棿鐐�
-     */
-    private double calculateScreenshotTime(double durationSeconds) {
-        // 1. 灏濊瘯鍦ㄧ1绉�
-        // 2. 濡傛灉瑙嗛涓嶈冻1绉掞紝鍦ㄤ腑闂存埅鍥�
-        // 3. 濡傛灉瑙嗛寰堢煭锛�<0.5绉掞級锛屽湪寮�澶存埅鍥�
-        double screenshotTime = 1.0; // 榛樿绗�1绉�
-        if (durationSeconds < 1.0) {
-            screenshotTime = durationSeconds / 2; // 涓棿
-        }
-        if (durationSeconds < 0.5) {
-            screenshotTime = 0; // 寮�澶�
-        }
-        return screenshotTime;
-    }
-
-    /**
-     * 鑾峰彇瑙嗛淇℃伅
-     */
-    private MultimediaInfo getVideoInfo(File videoFile) throws Exception {
-        MultimediaObject multimediaObject = new MultimediaObject(videoFile);
-        return multimediaObject.getInfo();
-    }
-
-
-    /**
-     * 鎵归噺鐢熸垚缂╃暐鍥�
-     */
-    public BatchThumbnailResult batchGenerateThumbnails(List<MultipartFile> files) {
-        BatchThumbnailResult result = new BatchThumbnailResult();
-        List<VideoProcessResult> results = new ArrayList<>();
-
-        int successCount = 0;
-        int failCount = 0;
-
-        for (MultipartFile file : files) {
-            try {
-                VideoProcessResult singleResult = processVideo(file);
-                results.add(singleResult);
-
-                if (singleResult.isSuccess()) {
-                    successCount++;
-                } else {
-                    failCount++;
-                }
-
-            } catch (Exception e) {
-                log.error("澶勭悊瑙嗛澶辫触: {}", file.getOriginalFilename(), e);
-                failCount++;
-            }
-        }
-
-        result.setResults(results);
-        result.setTotal(files.size());
-        result.setSuccess(successCount);
-        result.setFail(failCount);
-
-        return result;
-    }
-
-    /**
-     * 浠庤棰戠洰褰曟壒閲忕敓鎴愮缉鐣ュ浘
-     */
-    public void generateThumbnailsFromDirectory(File videoDir) throws Exception {
-        if (!videoDir.exists() || !videoDir.isDirectory()) {
-            throw new IllegalArgumentException("瑙嗛鐩綍涓嶅瓨鍦�");
-        }
-
-        File[] videoFiles = videoDir.listFiles((dir, name) -> isVideoFile(new File(dir, name)));
-
-        if (videoFiles == null || videoFiles.length == 0) {
-            log.warn("瑙嗛鐩綍涓病鏈夋壘鍒版敮鎸佺殑瑙嗛鏂囦欢");
-            return;
-        }
-
-        log.info("寮�濮嬫壒閲忕敓鎴愮缉鐣ュ浘锛屽叡{}涓棰�", videoFiles.length);
-
-        int successCount = 0;
-        int failCount = 0;
-
-        for (File videoFile : videoFiles) {
-            try {
-                generateThumbnail(videoFile.getPath());
-                successCount++;
-                log.info("宸茬敓鎴愮缉鐣ュ浘: {}", videoFile.getName());
-            } catch (Exception e) {
-                failCount++;
-                log.error("鐢熸垚缂╃暐鍥惧け璐�: {}", videoFile.getName(), e);
-            }
-        }
-
-        log.info("鎵归噺鐢熸垚缂╃暐鍥惧畬鎴愶紝鎴愬姛{}涓紝澶辫触{}涓�", successCount, failCount);
-    }
+//    public BatchThumbnailResult batchGenerateThumbnails(List<MultipartFile> files) {
+//        BatchThumbnailResult result = new BatchThumbnailResult();
+//        List<VideoProcessResult> results = new ArrayList<>();
+//
+//        int successCount = 0;
+//        int failCount = 0;
+//
+//        for (MultipartFile file : files) {
+//            try {
+//                VideoProcessResult singleResult = processVideo(file);
+//                results.add(singleResult);
+//
+//                if (singleResult.isSuccess()) {
+//                    successCount++;
+//                } else {
+//                    failCount++;
+//                }
+//
+//            } catch (Exception e) {
+//                log.error("澶勭悊瑙嗛澶辫触: {}", file.getOriginalFilename(), e);
+//
+//                VideoProcessResult errorResult = new VideoProcessResult();
+//                errorResult.setSuccess(false);
+//                errorResult.setMessage("澶勭悊澶辫触: " + e.getMessage());
+//                errorResult.setOriginalFilename(file.getOriginalFilename());
+//                results.add(errorResult);
+//
+//                failCount++;
+//            }
+//        }
+//
+//        result.setResults(results);
+//        result.setTotal(files.size());
+//        result.setSuccess(successCount);
+//        result.setFail(failCount);
+//
+//        return result;
+//    }
 
     /**
      * 鑾峰彇瑙嗛鏃堕暱
@@ -361,7 +729,7 @@
         if (fileName == null) return "";
         int lastDot = fileName.lastIndexOf('.');
         if (lastDot > 0 && lastDot < fileName.length() - 1) {
-            return fileName.substring(lastDot + 1);
+            return fileName.substring(lastDot + 1).toLowerCase();
         }
         return "";
     }
@@ -379,63 +747,58 @@
     }
 
     /**
-     * 鏍规嵁鍥剧墖鏍煎紡鑾峰彇缂栫爜鍣�
+     * 楠岃瘉鍥剧墖鏄惁鏈夋晥
      */
-    private String getCodecForFormat(String format) {
+    private boolean isValidImage(byte[] imageData) {
+        try (ByteArrayInputStream bis = new ByteArrayInputStream(imageData)) {
+            BufferedImage image = ImageIO.read(bis);
+            return image != null;
+        } catch (Exception e) {
+            return false;
+        }
+    }
+
+    /**
+     * 鑾峰彇MIME绫诲瀷
+     */
+    private String getMimeType(String format) {
         switch (format.toLowerCase()) {
             case "jpg":
             case "jpeg":
-                return "mjpeg";
+                return "image/jpeg";
             case "png":
-                return "png";
+                return "image/png";
             case "gif":
-                return "gif";
+                return "image/gif";
             case "bmp":
-                return "bmp";
+                return "image/bmp";
+            case "webp":
+                return "image/webp";
             default:
-                return "png";
+                return "image/jpeg";
         }
     }
 
     /**
-     * 鏍规嵁鎵╁睍鍚嶈幏鍙栨牸寮�
+     * 鍒ゆ柇鎿嶄綔绯荤粺鏄惁鏄疻indows
      */
-    private String getFormatForExtension(String extension) {
-        switch (extension.toLowerCase()) {
-            case "jpg":
-            case "jpeg":
-                return "image2";
-            case "png":
-                return "image2";
-            case "gif":
-                return "gif";
-            case "bmp":
-                return "bmp";
-            default:
-                return "image2";
-        }
-    }
-
-    /**
-     * 鍒涘缓鐩綍
-     */
-    private void createDirectories() throws IOException {
-        Files.createDirectories(Paths.get(uploadDir));
-        Files.createDirectories(Paths.get(thumbnailDir));
+    private boolean isWindows() {
+        String os = System.getProperty("os.name").toLowerCase();
+        return os.contains("win");
     }
 
     @Data
     public static class VideoProcessResult {
         private boolean success;
         private String message;
-        private String originalPath;
         private String originalFilename;
-        private String thumbnailPath;
-        private List<String> thumbnailPaths; // 澶氫釜灏侀潰璺緞
+        private String thumbnailBase64;  // Base64缂栫爜鐨勫皝闈�
+        private String mimeType;         // 灏侀潰MIME绫诲瀷
         private long fileSize;          // 鍘熷鏂囦欢澶у皬
         private double duration;        // 瑙嗛鏃堕暱锛堢锛�
         private String resolution;      // 瑙嗛鍒嗚鲸鐜�
         private String videoFormat;     // 瑙嗛鏍煎紡
+        private String thumbnailSize;   // 缂╃暐鍥惧昂瀵�
 
         public String getFileSizeFormatted() {
             return formatFileSize(fileSize);
@@ -511,4 +874,3 @@
         }
     }
 }
-

--
Gitblit v1.9.1