zqy
15 小时以前 4aa4a9f3ddeccf7d8f05f54a127f0334b7691aaf
zhang-content/src/main/java/com/ruoyi/service/impl/VideoProcessService.java
@@ -1,5 +1,8 @@
package com.ruoyi.service.impl;
import com.drew.imaging.ImageMetadataReader;
import com.drew.metadata.Metadata;
import com.drew.metadata.exif.ExifIFD0Directory;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
@@ -15,6 +18,7 @@
import javax.imageio.ImageWriter;
import javax.imageio.stream.MemoryCacheImageOutputStream;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.*;
import java.nio.file.Files;
@@ -68,18 +72,125 @@
    /**
     * 读取图片并处理EXIF方向
     */
    private BufferedImage readImageWithExifOrientation(File file) throws Exception {
        // 读取原始图片
        BufferedImage originalImage = ImageIO.read(file);
        if (originalImage == null) {
            return null;
        }
        // 获取EXIF方向
        int orientation = 1;
        try {
            orientation = getExifOrientation(file);
        } catch (Exception e) {
            // 如果不能读取EXIF,使用默认方向
            log.warn("无法读取EXIF方向信息: {}", e.getMessage());
        }
        // 根据方向旋转图片
        return rotateImageByExifOrientation(originalImage, orientation);
    }
    /**
     * 获取EXIF方向信息
     */
    private int getExifOrientation(File file) throws Exception {
        Metadata metadata = ImageMetadataReader.readMetadata(file);
        ExifIFD0Directory exifIFD0Directory = metadata.getFirstDirectoryOfType(ExifIFD0Directory.class);
        if (exifIFD0Directory != null && exifIFD0Directory.containsTag(ExifIFD0Directory.TAG_ORIENTATION)) {
            return exifIFD0Directory.getInt(ExifIFD0Directory.TAG_ORIENTATION);
        }
        return 1; // 默认方向
    }
    /**
     * 根据EXIF方向旋转图片
     */
    private BufferedImage rotateImageByExifOrientation(BufferedImage image, int orientation) {
        int width = image.getWidth();
        int height = image.getHeight();
        AffineTransform transform = new AffineTransform();
        switch (orientation) {
            case 1: // 正常
                return image;
            case 2: // 水平翻转
                transform.scale(-1, 1);
                transform.translate(-width, 0);
                break;
            case 3: // 旋转180度
                transform.translate(width, height);
                transform.rotate(Math.PI);
                break;
            case 4: // 垂直翻转
                transform.scale(1, -1);
                transform.translate(0, -height);
                break;
            case 5: // 顺时针旋转90度 + 水平翻转
                transform.rotate(-Math.PI / 2);
                transform.scale(-1, 1);
                break;
            case 6: // 顺时针旋转90度
                transform.rotate(Math.PI / 2);
                transform.translate(0, -height);
                break;
            case 7: // 顺时针旋转270度 + 水平翻转
                transform.rotate(Math.PI / 2);
                transform.scale(-1, 1);
                transform.translate(-width, 0);
                break;
            case 8: // 顺时针旋转270度
                transform.rotate(-Math.PI / 2);
                transform.translate(-width, 0);
                break;
            default:
                return image;
        }
        // 计算新图片尺寸
        double newWidth, newHeight;
        if (orientation >= 5 && orientation <= 8) {
            // 旋转了90或270度,宽高互换
            newWidth = height;
            newHeight = width;
        } else {
            newWidth = width;
            newHeight = height;
        }
        BufferedImage rotatedImage = new BufferedImage(
            (int) Math.ceil(newWidth),
            (int) Math.ceil(newHeight),
            image.getType()
        );
        Graphics2D g2d = rotatedImage.createGraphics();
        g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
            RenderingHints.VALUE_INTERPOLATION_BILINEAR);
        g2d.drawImage(image, transform, null);
        g2d.dispose();
        return rotatedImage;
    }
    /**
     * 处理图片文件
     */
    public Map<String, Object> processImage(File file, int width, int height,
                                            float quality, String format,boolean deleteY) throws IOException {
                                            float quality, String format) throws Exception {
        Map<String, Object> result = new HashMap<>();
        // 读取图片
        BufferedImage image = ImageIO.read(file);
        BufferedImage image = readImageWithExifOrientation(file);
        if (image == null) {
            result.put("success", false);
            result.put("message", "无法读取图片文件");
@@ -108,11 +219,6 @@
        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;
    }
@@ -225,29 +331,57 @@
    }
    /**
     * 压缩为PNG格式
     * 压缩为PNG格式(兼容性修复)
     */
    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);
            // 如果没有PNG编码器,使用默认方式
            boolean written = ImageIO.write(image, "png", output);
            if (!written) {
                throw new IOException("无法写入PNG格式");
            }
            return;
        }
        ImageWriter writer = writers.next();
        ImageWriteParam param = writer.getDefaultWriteParam();
        try {
            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)));
            // PNG的压缩级别(0-9,0最快但压缩率低,9最慢但压缩率高)
            if (compressionLevel >= 0) {
                try {
                    // 先检查是否支持压缩模式
                    if (param.canWriteCompressed()) {
                        param.setCompressionMode(ImageWriteParam.MODE_EXPLICIT);
                        // 检查是否支持Deflate压缩类型
                        String[] compressionTypes = param.getCompressionTypes();
                        if (compressionTypes != null) {
                            for (String type : compressionTypes) {
                                if ("Deflate".equalsIgnoreCase(type)) {
                                    param.setCompressionType("Deflate");
                                    break;
                                }
                            }
                        }
                        param.setCompressionQuality(Math.max(0.0f, Math.min(1.0f, compressionLevel / 9.0f)));
                    } else {
                        // 如果不支持压缩,使用默认设置
                        log.debug("PNG编码器不支持压缩设置,使用默认压缩");
                    }
                } catch (UnsupportedOperationException e) {
                    // 捕获不支持的操作异常,继续使用默认设置
                    log.warn("PNG压缩设置不被支持: {}", e.getMessage());
                }
            }
            writer.setOutput(new MemoryCacheImageOutputStream(output));
            writer.write(null, new IIOImage(image, null, null), param);
        } finally {
            writer.dispose();
        }
        writer.setOutput(new MemoryCacheImageOutputStream(output));
        writer.write(null, new IIOImage(image, null, null), param);
        writer.dispose();
    }
@@ -259,7 +393,7 @@
     * 处理视频文件:生成Base64编码的封面 (使用JavaCV)
     */
    public Map<String, Object> processVideo(File file, int width, int height,
                                            float quality, String format, boolean deleteY) {
                                            float quality, String format) {
        Map<String, Object> result = new HashMap<>();
        try {
@@ -300,15 +434,6 @@
            log.error("视频封面生成失败", e);
            result.put("success", false);
            result.put("message", "封面生成失败: " + e.getMessage());
        } finally {
            // 清理临时文件
            if (deleteY && file != null && file.exists()) {
                try {
                    file.delete();
                } catch (Exception e) {
                    log.warn("删除临时视频文件时出错", e);
                }
            }
        }
        return result;
    }