common-api/src/main/java/cn/lili/controller/lmk/LmkFileController.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
framework/pom.xml | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
framework/src/main/java/cn/lili/modules/lmk/service/LmkFileService.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
framework/src/main/java/cn/lili/modules/lmk/service/impl/LmkFileServiceImpl.java | ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史 | |
framework/src/main/java/cn/lili/utils/COSUtil.java | ●●●●● 补丁 | 查看 | 原始文档 | 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