From 7c0603315e57e3765270a8ac6b310b5a32af5a40 Mon Sep 17 00:00:00 2001
From: zqy <2522236926@qq.com>
Date: 星期五, 02 一月 2026 15:11:13 +0800
Subject: [PATCH] 新增压缩图 和 视频封面 都返回base64
---
ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java | 664 +++----------------------
ruoyi-common/src/main/java/com/ruoyi/common/config/ElasticSearchConfig.java | 1
zhang-content/src/main/java/com/ruoyi/service/impl/VideoProcessService.java | 888 ++++++++++++++++++++++++----------
3 files changed, 714 insertions(+), 839 deletions(-)
diff --git a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java
index 461ee1f..308d8e7 100644
--- a/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java
+++ b/ruoyi-admin/src/main/java/com/ruoyi/web/controller/common/CommonController.java
@@ -5,12 +5,18 @@
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
import java.util.*;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import javax.imageio.IIOImage;
+import javax.imageio.ImageWriteParam;
+import javax.imageio.ImageWriter;
+import javax.imageio.stream.MemoryCacheImageOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.websocket.server.PathParam;
@@ -35,14 +41,20 @@
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;
import com.ruoyi.framework.config.ServerConfig;
-
-
+import springfox.bean.validators.plugins.schema.NotNullAnnotationPlugin;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
-import java.io.*;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+import java.util.Base64;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
@@ -97,11 +109,19 @@
@Value("${thumbnail.keep-aspect-ratio:true}")
private boolean keepAspectRatio;
+ // 鏀寔鐨勫浘鐗囨牸寮�
+ private static final String[] IMAGE_FORMATS = {"jpg", "jpeg", "png", "gif", "bmp", "webp"};
+
+ // 鏀寔鐨勮棰戞牸寮�
+ private static final String[] VIDEO_FORMATS = {"mp4", "avi", "mov", "wmv", "flv", "mkv", "webm"};
+ @Autowired
+ private NotNullAnnotationPlugin notNullPlugin;
+
@Anonymous
@GetMapping("/generateThumbnail")
public AjaxResult generateThumbnail(@PathParam(value = "url") String url) throws Exception {
- return AjaxResult.success( videoProcessService.generateThumbnail(url));
+ return AjaxResult.success( );
}
// @GetMapping("/downloadFile")
// public void fileDownload(@PathParam("path") String path, HttpServletResponse response)
@@ -493,452 +513,99 @@
-
-
-
- // 鏀寔鐨勫浘鐗囨牸寮�
- private static final Set<String> SUPPORTED_IMAGE_FORMATS =
- new HashSet<>(Arrays.asList("jpg", "jpeg", "png", "gif", "bmp", "webp", "tiff")); // 缂╃暐鍥剧紦瀛�
- private final Map<String, Long> thumbnailCache = new HashMap<>();
-
/**
- * 鍔ㄦ�佺敓鎴愮缉鐣ュ浘 - 涓诲叆鍙�
- * 鍙傛暟璇存槑锛�
- * path: 鍥剧墖璺緞锛堝繀闇�锛�
- * width: 缂╃暐鍥惧搴︼紙鍙�夛紝榛樿300锛�
- * height: 缂╃暐鍥鹃珮搴︼紙鍙�夛紝榛樿200锛�
- * mode: 鐢熸垚妯″紡锛堝彲閫夛紝crop=瑁佸壀锛宻cale=缂╂斁锛岄粯璁cale锛�
- * quality: 鍥剧墖璐ㄩ噺锛堝彲閫夛紝0-1锛岄粯璁�0.8锛�
- * format: 杈撳嚭鏍煎紡锛堝彲閫夛紝jpg/png绛夛紝榛樿jpg锛�
+ * 鍘嬬缉鍥剧墖鎴栨彁鍙栬棰戝皝闈㈠苟杩斿洖Base64
+ *
+ * @param file 鍥剧墖鎴栬棰戞枃浠�
+ * @param width 鐩爣瀹藉害锛岄粯璁�100
+ * @param height 鐩爣楂樺害锛岄粯璁�100
+ * @param quality 鍥剧墖璐ㄩ噺 0.1-1.0锛岄粯璁�0.8
+ * @return Map鍖呭惈鍘嬬缉缁撴灉鍜孊ase64
*/
@Anonymous
- @GetMapping("/thumbnail")
- public void generateThumbnail(
- @RequestParam("path") String imagePath,
- @RequestParam(value = "width", required = false) Integer width,
- @RequestParam(value = "height", required = false) Integer height,
- @RequestParam(value = "mode", defaultValue = "scale") String mode,
- @RequestParam(value = "quality", required = false) Double quality,
- @RequestParam(value = "format", required = false) String format,
+ @PostMapping(value = "/zip", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+ public Map<String, Object> compressMediaToBase64(
+ @RequestParam(value = "file", required = false) MultipartFile file,
+ @RequestParam(value = "filePath", required = false) String filePath,
+ @RequestParam(value = "width", defaultValue = "0") int width,
+ @RequestParam(value = "height", defaultValue = "0") int height,
+ @RequestParam(value = "quality", defaultValue = "0.8") float quality) {
+ Map<String, Object> result = new HashMap<>();
- HttpServletResponse response) {
+ File trueFile;
+ boolean deleteY = true;
try {
- // 1. 鍙傛暟楠岃瘉鍜岃缃粯璁ゅ��
- String decodedPath = URLDecoder.decode(imagePath, "UTF-8").replace("/profile","");
-
- int targetWidth = width != null ? Math.min(width, maxThumbnailWidth) : defaultThumbnailWidth;
- int targetHeight = height != null ? Math.min(height, maxThumbnailHeight) : defaultThumbnailHeight;
- double targetQuality = quality != null ? Math.max(0.1, Math.min(1.0, quality)) : thumbnailQuality;
- String targetFormat = format != null && SUPPORTED_IMAGE_FORMATS.contains(format.toLowerCase())
- ? format.toLowerCase() : thumbnailFormat;
-
- // 2. 瀹夊叏妫�鏌�
- if (!FileUtils.checkAllowDownload(decodedPath)) {
- response.sendError(HttpServletResponse.SC_FORBIDDEN, "绂佹璁块棶璇ヨ矾寰�");
- return;
+ // 楠岃瘉鏂囦欢
+ if ((file == null || file.isEmpty()) && filePath == null ) {
+ result.put("success", false);
+ result.put("message", "鏂囦欢涓嶈兘涓虹┖");
+ return result;
}
- // 3. 鑾峰彇鍘熷浘鏂囦欢
- String fullPath = RuoYiConfig.getProfile() + decodedPath;
- File originalFile = new File(fullPath);
-
- if (!originalFile.exists()) {
- response.sendError(HttpServletResponse.SC_NOT_FOUND, "鍥剧墖涓嶅瓨鍦�: " + decodedPath);
- return;
+ if ((file == null || file.isEmpty())){
+ deleteY = false;
+ String fileUel = RuoYiConfig.getProfile() + filePath.replace("/profile","");
+ trueFile = new File(fileUel);
+ }else {
+ trueFile = videoProcessService.convertToFile(file);
+ }
+ String fileName = trueFile.getName();
+ if (fileName.isEmpty() && filePath == null) {
+ result.put("success", false);
+ result.put("message", "鏂囦欢鍚嶄笉鑳戒负绌�");
+ return result;
}
- if (!isImageFile(originalFile)) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "涓嶆槸鍥剧墖鏂囦欢: " + originalFile.getName());
- return;
- }
+ System.out.println("-----------"+fileName);
+ // 鑾峰彇鏂囦欢鎵╁睍鍚�
+ String extension = getFileExtension(fileName).toLowerCase();
+ System.out.println("-----------"+extension);
-
- // 5. 鐢熸垚缂╃暐鍥�
- BufferedImage thumbnail = generateThumbnailImage(
- originalFile, targetWidth, targetHeight, mode, targetQuality, targetFormat
- );
-
- if (thumbnail == null) {
- response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "鐢熸垚缂╃暐鍥惧け璐�");
- return;
- }
-
- // 7. 杈撳嚭缂╃暐鍥�
- sendThumbnailResponse(thumbnail, targetFormat, targetQuality, response);
-
- } catch (Exception e) {
- log.error("鐢熸垚缂╃暐鍥惧け璐�: path={}, error={}", imagePath, e.getMessage(), e);
- try {
- response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "鐢熸垚缂╃暐鍥惧け璐�: " + e.getMessage());
- } catch (IOException ex) {
- log.error("鍙戦�侀敊璇搷搴斿け璐�", ex);
- }
- }
- }
-
- /**
- * 鎵归噺鐢熸垚缂╃暐鍥� - 閫氳繃閫楀彿鍒嗛殧鐨勮矾寰�
- * 鍙傛暟璇存槑锛�
- * paths: 鐢ㄩ�楀彿鍒嗛殧鐨勫浘鐗囪矾寰勫垪琛紙蹇呴渶锛�
- * width: 缂╃暐鍥惧搴︼紙鍙�夛紝榛樿300锛�
- * height: 缂╃暐鍥鹃珮搴︼紙鍙�夛紝榛樿200锛�
- * mode: 鐢熸垚妯″紡锛堝彲閫夛紝crop=瑁佸壀锛宻cale=缂╂斁锛岄粯璁cale锛�
- * quality: 鍥剧墖璐ㄩ噺锛堝彲閫夛紝0-1锛岄粯璁�0.8锛�
- * format: 杈撳嚭鏍煎紡锛堝彲閫夛紝jpg/png绛夛紝榛樿jpg锛�
- */
- @Anonymous
- @PostMapping("/batchThumbnailByPaths")
- public AjaxResult generateBatchThumbnailByPaths(
- @RequestParam("paths") String imagePaths,
- @RequestParam(value = "width", required = false) Integer width,
- @RequestParam(value = "height", required = false) Integer height,
- @RequestParam(value = "mode", defaultValue = "scale") String mode,
- @RequestParam(value = "quality", required = false) Double quality,
- @RequestParam(value = "format", required = false) String format) {
-
- List<Map<String, Object>> results = new ArrayList<>();
- List<String> thumbnailUrls = new ArrayList<>(); // 瀛樺偍鎵�鏈夌缉鐣ュ浘URL
-
- try {
- // 1. 鍒嗗壊璺緞
- String[] pathArray = imagePaths.split(",");
- if (pathArray.length == 0) {
- return AjaxResult.error("鍥剧墖璺緞涓嶈兘涓虹┖");
- }
-
- // 2. 璁剧疆鍘嬬缉鍙傛暟
- int targetWidth = width != null ? Math.min(width, maxThumbnailWidth) : defaultThumbnailWidth;
- int targetHeight = height != null ? Math.min(height, maxThumbnailHeight) : defaultThumbnailHeight;
- double targetQuality = quality != null ? Math.max(0.1, Math.min(1.0, quality)) : thumbnailQuality;
- String targetFormat = format != null && SUPPORTED_IMAGE_FORMATS.contains(format.toLowerCase())
- ? format.toLowerCase() : thumbnailFormat;
-
- int successCount = 0;
- int failCount = 0;
-
- // 3. 澶勭悊姣忎釜璺緞
- for (String path : pathArray) {
- Map<String, Object> result = new HashMap<>();
- String trimmedPath = path.trim();
-
- if (trimmedPath.isEmpty()) {
- continue;
- }
-
- result.put("originalPath", trimmedPath);
-
- try {
- // 瑙g爜璺緞
- String decodedPath = URLDecoder.decode(trimmedPath, "UTF-8").replace("/profile","");
-
- // 瀹夊叏妫�鏌�
- if (!FileUtils.checkAllowDownload(decodedPath)) {
- result.put("success", false);
- result.put("error", "绂佹璁块棶璇ヨ矾寰�");
- result.put("code", 403);
- results.add(result);
- failCount++;
- continue;
- }
-
- // 鑾峰彇鍘熷浘
- String fullPath = RuoYiConfig.getProfile() + decodedPath;
- File originalFile = new File(fullPath);
-
- if (!originalFile.exists()) {
- result.put("success", false);
- result.put("error", "鍥剧墖涓嶅瓨鍦�");
- result.put("code", 404);
- results.add(result);
- failCount++;
- continue;
- }
-
- if (!isImageFile(originalFile)) {
- result.put("success", false);
- result.put("error", "涓嶆槸鍥剧墖鏂囦欢");
- result.put("code", 400);
- results.add(result);
- failCount++;
- continue;
- }
-
- // 鐢熸垚缂╃暐鍥綰RL
- String thumbnailUrl = buildThumbnailUrlWithParams(
- trimmedPath, targetWidth, targetHeight, mode, targetQuality, targetFormat
- );
-
- result.put("success", true);
- result.put("thumbnailUrl", thumbnailUrl);
- result.put("originalUrl", serverConfig.getUrl() + decodedPath);
- result.put("fileName", originalFile.getName());
- result.put("code", 200);
-
- // 灏嗘垚鍔熺殑缂╃暐鍥綰RL娣诲姞鍒板垪琛�
- thumbnailUrls.add(thumbnailUrl);
-
- // 鑾峰彇鍥剧墖淇℃伅
- BufferedImage originalImage = ImageIO.read(originalFile);
- if (originalImage != null) {
- result.put("originalWidth", originalImage.getWidth());
- result.put("originalHeight", originalImage.getHeight());
- }
-
- // 鍘嬬缉鍙傛暟
-
- Map<String, Object> compressParams = new HashMap<>();
- compressParams.put("width", targetWidth);
- compressParams.put("height", targetHeight);
- compressParams.put("mode", mode);
- compressParams.put("quality", targetQuality);
- compressParams.put("format", targetFormat);
-
- result.put("compressParams", compressParams);
-
- successCount++;
-
- } catch (Exception e) {
- log.error("澶勭悊缂╃暐鍥捐姹傚け璐�: {}", trimmedPath, e);
- result.put("success", false);
- result.put("error", e.getMessage());
- result.put("code", 500);
- failCount++;
- }
-
- results.add(result);
- }
-
- // 4. 鏋勫缓杩斿洖缁撴灉
- Map<String, Object> responseData = new HashMap<>();
- responseData.put("results", results);
-
- // 灏嗘墍鏈夋垚鍔熺殑缂╃暐鍥綰RL鐢ㄩ�楀彿杩炴帴
- if (!thumbnailUrls.isEmpty()) {
- responseData.put("thumbnailUrls", String.join(",", thumbnailUrls));
+ // 鍒ゆ柇鏂囦欢绫诲瀷
+ if (isImageFile(extension)) {
+ // 澶勭悊鍥剧墖鏂囦欢
+ return videoProcessService.processImage(trueFile, width, height, quality, extension,deleteY);
+ } else if (isVideoFile(extension)) {
+ // 澶勭悊瑙嗛鏂囦欢
+ return videoProcessService.processVideo(trueFile,width,height,quality,extension,deleteY);
} else {
- responseData.put("thumbnailUrls", "");
- }
-
- // 姹囨�讳俊鎭�
- Map<String, Object> summary = new HashMap<>();
- summary.put("total", pathArray.length);
- summary.put("success", successCount);
- summary.put("fail", failCount);
- Map<String, Object> compressParams = new HashMap<>();
- compressParams.put("width", targetWidth);
- compressParams.put("height", targetHeight);
- compressParams.put("mode", mode);
- compressParams.put("quality", targetQuality);
- compressParams.put("format", targetFormat);
-
- summary.put("compressParams", compressParams);
-
- responseData.put("summary", summary);
-
- return AjaxResult.success(
- String.format("鎵归噺澶勭悊瀹屾垚锛屾垚鍔�%s涓紝澶辫触%s涓�", successCount, failCount),
- responseData
- );
-
- } catch (Exception e) {
- log.error("鎵归噺鐢熸垚缂╃暐鍥惧け璐�", e);
- return AjaxResult.error("鎵归噺澶勭悊澶辫触: " + e.getMessage());
- }
- }
-
-
- /**
- * 鎵归噺鐢熸垚缂╃暐鍥惧苟鐩存帴鍘嬬缉鍥剧墖 - 杩斿洖鍘嬬缉鍚庣殑鍥剧墖淇℃伅
- */
- @Anonymous
- @PostMapping("/batchCompressImages")
- public void batchCompressImages(
- @RequestParam("paths") String imagePaths,
- @RequestParam(value = "width", required = false) Integer width,
- @RequestParam(value = "height", required = false) Integer height,
- @RequestParam(value = "mode", defaultValue = "scale") String mode,
- @RequestParam(value = "quality", required = false) Double quality,
- @RequestParam(value = "format", required = false) String format,
- HttpServletResponse response) {
-
- try {
- // 1. 鍒嗗壊璺緞
- String[] pathArray = imagePaths.split(",");
- if (pathArray.length == 0) {
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, "鍥剧墖璺緞涓嶈兘涓虹┖");
- return;
- }
-
- // 2. 璁剧疆鍘嬬缉鍙傛暟
- int targetWidth = width != null ? Math.min(width, maxThumbnailWidth) : defaultThumbnailWidth;
- int targetHeight = height != null ? Math.min(height, maxThumbnailHeight) : defaultThumbnailHeight;
- double targetQuality = quality != null ? Math.max(0.1, Math.min(1.0, quality)) : thumbnailQuality;
- String targetFormat = format != null && SUPPORTED_IMAGE_FORMATS.contains(format.toLowerCase())
- ? format.toLowerCase() : thumbnailFormat;
-
- // 3. 鐢熸垚ZIP鍘嬬缉鍖�
- response.setContentType("application/zip");
- response.setHeader("Content-Disposition", "attachment; filename=\"compressed_images.zip\"");
-
- try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
- int processedCount = 0;
-
- for (String path : pathArray) {
- String trimmedPath = path.trim();
- if (trimmedPath.isEmpty()) {
- continue;
- }
-
- try {
- // 瑙g爜璺緞
- String decodedPath = URLDecoder.decode(trimmedPath, "UTF-8").replace("/profile","");
-
- // 瀹夊叏妫�鏌�
- if (!FileUtils.checkAllowDownload(decodedPath)) {
- log.warn("绂佹璁块棶璺緞: {}", decodedPath);
- continue;
- }
-
- // 鑾峰彇鍘熷浘
- String fullPath = RuoYiConfig.getProfile() + decodedPath;
- File originalFile = new File(fullPath);
-
- if (!originalFile.exists() || !isImageFile(originalFile)) {
- log.warn("鍥剧墖涓嶅瓨鍦ㄦ垨涓嶆槸鍥剧墖鏂囦欢: {}", decodedPath);
- continue;
- }
-
- // 鐢熸垚缂╃暐鍥�
- BufferedImage thumbnail = generateThumbnailImage(
- originalFile, targetWidth, targetHeight, mode, targetQuality, targetFormat
- );
-
- if (thumbnail != null) {
- // 娣诲姞鍒癦IP
- String fileName = getFileNameWithoutExtension(originalFile.getName()) +
- "_" + targetWidth + "x" + targetHeight +
- "." + targetFormat;
-
- ZipEntry zipEntry = new ZipEntry(fileName);
- zipOut.putNextEntry(zipEntry);
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ImageIO.write(thumbnail, targetFormat, baos);
- zipOut.write(baos.toByteArray());
-
- zipOut.closeEntry();
- processedCount++;
- }
-
- } catch (Exception e) {
- log.error("澶勭悊鍥剧墖澶辫触: {}", trimmedPath, e);
- }
- }
-
- if (processedCount == 0) {
- response.reset(); // 娓呯┖鍝嶅簲
- response.setContentType("application/json");
- response.getWriter().write("{\"code\": 500, \"msg\": \"娌℃湁鍥剧墖澶勭悊鎴愬姛\"}");
- }
-
- } catch (Exception e) {
- log.error("鐢熸垚鍘嬬缉鍖呭け璐�", e);
- response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "鐢熸垚鍘嬬缉鍖呭け璐�");
+ result.put("success", false);
+ result.put("message", "涓嶆敮鎸佺殑鏂囦欢鏍煎紡锛�" + extension);
+ return result;
}
} catch (Exception e) {
- log.error("鎵归噺鍘嬬缉鍥剧墖澶辫触", e);
- try {
- response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "澶勭悊澶辫触: " + e.getMessage());
- } catch (IOException ex) {
- log.error("鍙戦�侀敊璇搷搴斿け璐�", ex);
- }
+ log.error("鏂囦欢澶勭悊澶辫触", e);
+ result.put("success", false);
+ result.put("message", "澶勭悊澶辫触: " + e.getMessage());
+ return result;
}
}
/**
- * 鑾峰彇鍥剧墖淇℃伅锛堝寘鍚缉鐣ュ浘URL锛�
+ * 鍒ゆ柇鏄惁鏄浘鐗囨枃浠�
*/
- @Anonymous
- @GetMapping("/imageInfo")
- public AjaxResult getImageInfo(
- @RequestParam("path") String imagePath,
- @RequestParam(value = "width", required = false) Integer width,
- @RequestParam(value = "height", required = false) Integer height) {
-
- try {
- String decodedPath = URLDecoder.decode(imagePath, "UTF-8");
-
- // 瀹夊叏妫�鏌�
- if (!FileUtils.checkAllowDownload(decodedPath)) {
- return AjaxResult.error("绂佹璁块棶璇ヨ矾寰�");
+ private boolean isImageFile(String extension) {
+ for (String format : IMAGE_FORMATS) {
+ if (format.equalsIgnoreCase(extension)) {
+ return true;
}
-
- // 鑾峰彇鍘熷浘
- String fullPath = RuoYiConfig.getProfile() + decodedPath;
- File originalFile = new File(fullPath);
-
- if (!originalFile.exists()) {
- return AjaxResult.error("鍥剧墖涓嶅瓨鍦�");
- }
-
- if (!isImageFile(originalFile)) {
- return AjaxResult.error("涓嶆槸鍥剧墖鏂囦欢");
- }
-
- // 璇诲彇鍥剧墖淇℃伅
- BufferedImage image = ImageIO.read(originalFile);
- if (image == null) {
- return AjaxResult.error("鏃犳硶璇诲彇鍥剧墖");
- }
-
- // 鏋勫缓杩斿洖淇℃伅
- Map<String, Object> info = new HashMap<>();
- info.put("originalUrl", serverConfig.getUrl() + decodedPath);
- info.put("originalPath", decodedPath);
- info.put("fileName", originalFile.getName());
- info.put("fileSize", originalFile.length());
- info.put("fileType", getFileExtension(originalFile.getName()));
- info.put("width", image.getWidth());
- info.put("height", image.getHeight());
- info.put("lastModified", originalFile.lastModified());
-
- // 鏋勫缓缂╃暐鍥綰RL
- int targetWidth = width != null ? width : defaultThumbnailWidth;
- int targetHeight = height != null ? height : defaultThumbnailHeight;
-
- String thumbnailUrl = buildThumbnailUrl(decodedPath, targetWidth, targetHeight);
- info.put("thumbnailUrl", thumbnailUrl);
-
- // 涓嶅悓灏哄鐨勭缉鐣ュ浘URL
- info.put("smallThumbnailUrl", buildThumbnailUrl(decodedPath, 150, 100));
- info.put("mediumThumbnailUrl", buildThumbnailUrl(decodedPath, 300, 200));
- info.put("largeThumbnailUrl", buildThumbnailUrl(decodedPath, 600, 400));
-
- return AjaxResult.success("鑾峰彇鎴愬姛", info);
-
- } catch (Exception e) {
- log.error("鑾峰彇鍥剧墖淇℃伅澶辫触: {}", imagePath, e);
- return AjaxResult.error("鑾峰彇澶辫触: " + e.getMessage());
}
+ return false;
}
-
-
/**
- * 妫�鏌ユ槸鍚︿负鍥剧墖鏂囦欢
+ * 鍒ゆ柇鏄惁鏄棰戞枃浠�
*/
- private boolean isImageFile(File file) {
- if (file == null || !file.exists()) {
- return false;
+ private boolean isVideoFile(String extension) {
+ for (String format : VIDEO_FORMATS) {
+ if (format.equalsIgnoreCase(extension)) {
+ return true;
+ }
}
-
- String fileName = file.getName().toLowerCase();
- String extension = getFileExtension(fileName);
-
- return SUPPORTED_IMAGE_FORMATS.contains(extension);
+ return false;
}
/**
@@ -957,158 +624,5 @@
}
- /**
- * 鐢熸垚缂╃暐鍥�
- */
- private BufferedImage generateThumbnailImage(File originalFile, int width, int height,
- String mode, double quality, String format) {
- try {
- BufferedImage originalImage = ImageIO.read(originalFile);
- if (originalImage == null) {
- return null;
- }
-
- int originalWidth = originalImage.getWidth();
- int originalHeight = originalImage.getHeight();
-
- // 璁$畻鐩爣灏哄
- int targetWidth = width;
- int targetHeight = height;
-
- if (keepAspectRatio && mode.equals("scale")) {
- // 淇濇寔瀹介珮姣旂缉鏀�
- double widthRatio = (double) width / originalWidth;
- double heightRatio = (double) height / originalHeight;
- double ratio = Math.min(widthRatio, heightRatio);
-
- targetWidth = (int) (originalWidth * ratio);
- targetHeight = (int) (originalHeight * ratio);
- } else if (mode.equals("crop")) {
- // 瑁佸壀妯″紡
- double widthRatio = (double) width / originalWidth;
- double heightRatio = (double) height / originalHeight;
- double ratio = Math.max(widthRatio, heightRatio);
-
- int cropWidth = (int) (width / ratio);
- int cropHeight = (int) (height / ratio);
-
- // 灞呬腑瑁佸壀
- int cropX = (originalWidth - cropWidth) / 2;
- int cropY = (originalHeight - cropHeight) / 2;
-
- BufferedImage cropped = originalImage.getSubimage(
- Math.max(0, cropX),
- Math.max(0, cropY),
- Math.min(cropWidth, originalWidth - cropX),
- Math.min(cropHeight, originalHeight - cropY)
- );
-
- originalImage = cropped;
- }
-
- // 鍒涘缓鐩爣鍥剧墖
- BufferedImage thumbnail = new BufferedImage(targetWidth, targetHeight,
- format.equals("png") ? BufferedImage.TYPE_INT_ARGB : BufferedImage.TYPE_INT_RGB);
-
- // 缁樺埗缂╃暐鍥�
- Graphics2D g2d = thumbnail.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, targetWidth, targetHeight, null);
- g2d.dispose();
-
- return thumbnail;
-
- } catch (Exception e) {
- log.error("鐢熸垚缂╃暐鍥惧け璐�: {}", originalFile.getAbsolutePath(), e);
- return null;
- }
- }
-
-
- /**
- * 鍙戦�佺缉鐣ュ浘鍝嶅簲
- */
- private void sendThumbnailResponse(BufferedImage thumbnail, String format,
- double quality, HttpServletResponse response) throws IOException {
-
- response.setContentType("image/" + format);
- response.setHeader("Cache-Control", "public, max-age=31536000"); // 缂撳瓨1骞�
- response.setHeader("X-Thumbnail-Cache", "MISS");
-
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- ImageIO.write(thumbnail, format, baos);
-
- byte[] imageBytes = baos.toByteArray();
- response.setContentLength(imageBytes.length);
- response.getOutputStream().write(imageBytes);
- }
-
- /**
- * 鏋勫缓缂╃暐鍥綰RL
- */
- private String buildThumbnailUrl(String imagePath, int width, int height) {
- try {
- return serverConfig.getUrl() + "/common/thumbnail?" +
- "path=" + URLEncoder.encode(imagePath, "UTF-8") +
- "&width=" + width +
- "&height=" + height;
- } catch (UnsupportedEncodingException e) {
- return "";
- }
- }
-
-
- /**
- * 鏋勫缓甯﹀弬鏁扮殑缂╃暐鍥綰RL
- */
- private String buildThumbnailUrlWithParams(String imagePath, int width, int height,
- String mode, double quality, String format)
- throws UnsupportedEncodingException {
-
- StringBuilder url = new StringBuilder();
- url.append(serverConfig.getUrl()).append("/common/thumbnail?");
- url.append("path=").append(URLEncoder.encode(imagePath, "UTF-8"));
- url.append("&width=").append(width);
- url.append("&height=").append(height);
- url.append("&mode=").append(mode);
- url.append("&quality=").append(quality);
- url.append("&format=").append(format);
-
- return url.toString();
- }
-
- /**
- * 鑾峰彇涓嶅甫鎵╁睍鍚嶇殑鏂囦欢鍚�
- */
- private String getFileNameWithoutExtension(String fileName) {
- if (fileName == null) {
- return "";
- }
-
- int lastDot = fileName.lastIndexOf('.');
- if (lastDot > 0) {
- return fileName.substring(0, lastDot);
- }
-
- return fileName;
- }
- /**
- * 缂╃暐鍥捐姹傚弬鏁扮被
- */
- @Data
- static class ThumbnailRequest {
- private String path; // 鍥剧墖璺緞
- private Integer width; // 瀹藉害
- private Integer height; // 楂樺害
- private String mode; // 妯″紡锛歴cale/crop
- private Double quality; // 璐ㄩ噺锛�0-1
- private String format; // 鏍煎紡
- }
}
diff --git a/ruoyi-common/src/main/java/com/ruoyi/common/config/ElasticSearchConfig.java b/ruoyi-common/src/main/java/com/ruoyi/common/config/ElasticSearchConfig.java
index 0bd9ae9..1b01eba 100644
--- a/ruoyi-common/src/main/java/com/ruoyi/common/config/ElasticSearchConfig.java
+++ b/ruoyi-common/src/main/java/com/ruoyi/common/config/ElasticSearchConfig.java
@@ -36,7 +36,6 @@
"localhost",
8087,
-//
"http"
)
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