zxl
5 天以前 7e9972d25b3d035201331d401c8e1a74d8f2d8e8
分享适配
5个文件已修改
142 ■■■■■ 已修改文件
common-api/src/main/java/cn/lili/controller/lmk/LmkFileController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/pom.xml 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/service/LmkFileService.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/modules/lmk/service/impl/LmkFileServiceImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
framework/src/main/java/cn/lili/utils/COSUtil.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
common-api/src/main/java/cn/lili/controller/lmk/LmkFileController.java
@@ -43,6 +43,11 @@
        return lmkFileService.uploadObject(file);
    }
    @GetMapping("/generateVideoCoverUrl/{id}")
    public Result generateVideoCoverUrl(@PathVariable("id") String videoId){
        return lmkFileService.generateVideoCoverUrl(videoId,null,null,null);
    }
    /**
     * 多文件上传
framework/pom.xml
@@ -15,6 +15,16 @@
    <packaging>jar</packaging>
    <dependencies>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>javacv-platform</artifactId>
            <version>1.5.9</version>
        </dependency>
        <dependency>
            <groupId>org.bytedeco</groupId>
            <artifactId>ffmpeg-platform</artifactId>
            <version>6.0-1.5.9</version>
        </dependency>
        <!-- cos  sts -->
        <dependency>
            <groupId>com.qcloud</groupId>
framework/src/main/java/cn/lili/modules/lmk/service/LmkFileService.java
@@ -18,6 +18,8 @@
 */
public interface LmkFileService extends IService<LmkFile> {
    Result generateVideoCoverUrl(String videoId, Long snapshotTime, Integer width, Integer height);
    /**
     * 上传单个文件
     *
framework/src/main/java/cn/lili/modules/lmk/service/impl/LmkFileServiceImpl.java
@@ -2,11 +2,14 @@
import cn.lili.base.Result;
import cn.lili.common.exception.ServiceException;
import cn.lili.common.utils.StringUtils;
import cn.lili.cos.CosSTS;
import cn.lili.modules.lmk.domain.entity.LmkFile;
import cn.lili.modules.lmk.domain.entity.Video;
import cn.lili.modules.lmk.domain.form.FileInfoForm;
import cn.lili.modules.lmk.domain.vo.LmkFileVO;
import cn.lili.modules.lmk.mapper.LmkFileMapper;
import cn.lili.modules.lmk.mapper.VideoMapper;
import cn.lili.modules.lmk.service.LmkFileService;
import cn.lili.utils.COSUtil;
import cn.lili.utils.FileUtil;
@@ -35,6 +38,29 @@
    private final LmkFileMapper lmkFileMapper;
    private final COSUtil cosUtil;
    private final VideoMapper videoMapper;
    @Override
    public Result generateVideoCoverUrl(String videoId, Long snapshotTime, Integer width, Integer height){
        Video video = videoMapper.selectById(videoId);
        if (video != null){
            if (StringUtils.isNotBlank(video.getCoverUrl())){
                return Result.ok().data(cosUtil.getPreviewUrl(video.getCoverUrl()));
            }
            //获得videoKey
            try {
                System.out.println(video.getVideoFileKey());
                MultipartFile file =cosUtil.captureVideoCoverAsMultipart(cosUtil.getPreviewUrl(video.getVideoFileKey()), width, height);
                LmkFileVO fileVo = (LmkFileVO) this.uploadObject(file).get("data");
                video.setCoverUrl(fileVo.getFileKey());
                videoMapper.updateById(video);
                return Result.ok().data(fileVo.getUrl());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return Result.ok();
    }
    @Override
    public Result uploadObject(MultipartFile file) {
framework/src/main/java/cn/lili/utils/COSUtil.java
@@ -18,9 +18,15 @@
import com.tencent.cloud.cos.util.Jackson;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.bytedeco.javacv.*;
import org.bytedeco.opencv.global.opencv_imgproc;
import org.bytedeco.opencv.opencv_core.Mat;
import org.bytedeco.opencv.opencv_core.Size;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URL;
@@ -39,6 +45,99 @@
    private final COSConfigProperty cosConfigProperty;
    /**
     * 从网络视频URL截取第一秒画面,返回MultipartFile类型
     * @param videoUrl 网络视频地址
     * @param width 封面宽度
     * @param height 封面高度
     * @return 封面图片的MultipartFile对象
     * @throws Exception 处理异常
     */
    public MultipartFile captureVideoCoverAsMultipart(String videoUrl, Integer width, Integer height) throws Exception {
        // 设置默认宽高
        int targetWidth = width != null && width > 0 ? width : 800;
        int targetHeight = height != null && height > 0 ? height : 600;
        // 生成唯一文件名(用于MultipartFile的原始文件名)
        String fileName = UUID.randomUUID().toString() + ".jpg";
        // 使用内存流处理图片,避免临时文件
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        FFmpegFrameGrabber grabber = null;
        try {
            // 初始化视频抓取器
            grabber = new FFmpegFrameGrabber(videoUrl);
            grabber.start();
            // 定位到第一秒
            grabber.setTimestamp(1000000); // 1秒 = 1,000,000微秒
            // 获取视频帧
            Frame frame = grabber.grabImage();
            if (frame == null) {
                throw new RuntimeException("无法获取视频帧,可能视频格式不支持或URL无效");
            }
            // 转换为Mat并调整尺寸
            OpenCVFrameConverter.ToMat converter = new OpenCVFrameConverter.ToMat();
            Mat mat = converter.convert(frame);
            Mat resizedMat = new Mat();
            opencv_imgproc.resize(mat, resizedMat, new Size(targetWidth, targetHeight));
            // 将处理后的帧写入内存流
            Java2DFrameConverter java2dConverter = new Java2DFrameConverter();
            ImageIO.write(
                    java2dConverter.getBufferedImage(converter.convert(resizedMat)),
                    "jpg",
                    outputStream
            );
            // 将内存流转换为MultipartFile
            ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
            return new MockMultipartFile(
                    "file",          // 表单字段名(可自定义)
                    fileName,        // 原始文件名
                    "image/jpeg",    // 文件类型
                    inputStream      // 文件流
            );
        } finally {
            // 释放资源
            if (grabber != null) {
                try {
                    grabber.stop();
                    grabber.release();
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            outputStream.close();
        }
    }
    /**
     * 从完整URL中提取COS文件key
     * @param fullUrl 完整URL
     * @return COS文件key
     */
    public String extractFileKeyFromUrl(String fullUrl) {
        // 去除协议和域名部分
        String endpoint = cosConfigProperty.getEndpoint();
        if (fullUrl.startsWith(endpoint)) {
            return fullUrl.substring(endpoint.length() + 1);
        }
        // 如果URL包含bucket名称
        String bucketUrl = "https://" + cosConfigProperty.getBucket() + "." + endpoint;
        if (fullUrl.startsWith(bucketUrl)) {
            return fullUrl.substring(bucketUrl.length() + 1);
        }
        // 如果已经是相对路径,直接返回
        return fullUrl;
    }
    /**
     * 获取sts临时访问凭证
     *
     * @return