瀏覽代碼

feat: 提交后端上传视频文件接口和新增视频转图片接口

WANGKANG 10 月之前
父節點
當前提交
81af265b98

+ 10 - 4
taais-modules/taais-biz/pom.xml

@@ -53,11 +53,17 @@
             <groupId>org.springframework</groupId>
             <artifactId>spring-webflux</artifactId>
         </dependency>
+        <dependency>
+            <groupId>org.bytedeco</groupId>
+            <artifactId>javacv-platform</artifactId>
+            <version>1.5.7</version>
+        </dependency>
+
         <!--        <dependency>-->
-<!--            <groupId>org.apache.httpcomponents</groupId>-->
-<!--            <artifactId>httpclient</artifactId>-->
-<!--            <version>4.5.14</version>-->
-<!--        </dependency>-->
+        <!--            <groupId>org.apache.httpcomponents</groupId>-->
+        <!--            <artifactId>httpclient</artifactId>-->
+        <!--            <version>4.5.14</version>-->
+        <!--        </dependency>-->
 
     </dependencies>
 

+ 7 - 0
taais-modules/taais-biz/src/main/java/com/taais/biz/constant/BizConstant.java

@@ -4,6 +4,13 @@ package com.taais.biz.constant;
  * @author allen
  */
 public class BizConstant {
+    // 视频文件存储目录
+    public static final String UPLOAD_DIR = "uploads/";
+    public class VideoStatus {
+        public static final String NOT_START = "0";
+        public static final String RUNNING = "1";
+        public static final String END = "2";
+    }
 
     public static final String TASK_FOLDER_PATH_HEAD = "/task/";
 

+ 57 - 1
taais-modules/taais-biz/src/main/java/com/taais/biz/controller/Video2imageController.java

@@ -1,11 +1,18 @@
 package com.taais.biz.controller;
 
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Date;
 import java.util.List;
+import java.util.UUID;
 
 import com.taais.biz.domain.bo.Video2imageBo;
 import com.taais.biz.domain.vo.Video2imageVo;
 import com.taais.biz.service.IVideo2imageService;
 import com.taais.common.core.core.page.PageResult;
+import com.taais.common.core.utils.StringUtils;
 import lombok.RequiredArgsConstructor;
 import jakarta.servlet.http.HttpServletResponse;
 import cn.dev33.satoken.annotation.SaCheckPermission;
@@ -18,6 +25,11 @@ import com.taais.common.log.enums.BusinessType;
 import com.taais.common.web.annotation.RepeatSubmit;
 import com.taais.common.web.core.BaseController;
 import jakarta.annotation.Resource;
+import org.springframework.web.multipart.MultipartFile;
+
+
+import static com.taais.biz.constant.BizConstant.UPLOAD_DIR;
+import static com.taais.biz.constant.BizConstant.VideoStatus.NOT_START;
 
 /**
  * 视频转图片Controller
@@ -30,6 +42,7 @@ import jakarta.annotation.Resource;
 @RestController
 @RequestMapping("/demo/video2image")
 public class Video2imageController extends BaseController {
+
     @Resource
     private IVideo2imageService video2imageService;
 
@@ -70,7 +83,20 @@ public class Video2imageController extends BaseController {
     @RepeatSubmit()
     @PostMapping
     public CommonResult<Void> add(@Validated @RequestBody Video2imageBo video2imageBo) {
-        boolean inserted = video2imageService.insert(video2imageBo);
+        Video2imageBo newVideo2imageBo = new Video2imageBo();
+        if (StringUtils.isEmpty(video2imageBo.getFileId())) {
+            return CommonResult.fail("请上传视频文件!");
+        }
+        newVideo2imageBo.setName(video2imageBo.getName());
+        newVideo2imageBo.setFileId(video2imageBo.getFileId());
+        newVideo2imageBo.setFps(video2imageBo.getFps());
+        newVideo2imageBo.setStartTime(new Date());
+        newVideo2imageBo.setStatus(NOT_START);
+        newVideo2imageBo.setPath(Paths.get(UPLOAD_DIR, video2imageBo.getFileId()).toString());
+        newVideo2imageBo.setOutPath(Paths.get(UPLOAD_DIR, video2imageBo.getName(), "images").toString());
+        newVideo2imageBo.setRemarks(video2imageBo.getRemarks());
+
+        boolean inserted = video2imageService.insert(newVideo2imageBo);
         if (!inserted) {
             return CommonResult.fail("新增视频转图片记录失败!");
         }
@@ -105,4 +131,34 @@ public class Video2imageController extends BaseController {
         }
         return CommonResult.success();
     }
+
+    @PostMapping("/upload")
+    public CommonResult uploadFile(@RequestParam("file") MultipartFile file) {
+        if (file.isEmpty()) {
+            return CommonResult.success("文件不能为空!");
+        }
+
+        try {
+            // 创建上传目录(如果不存在)
+            Path uploadPath = Paths.get(UPLOAD_DIR);
+            if (!Files.exists(uploadPath)) {
+                Files.createDirectories(uploadPath);
+            }
+
+            UUID uuid = UUID.randomUUID();
+            String uuidString = uuid.toString();
+
+            // 获取文件名并构建目标路径
+            String fileName = uuidString + "_" + file.getOriginalFilename();
+            Path targetPath = uploadPath.resolve(fileName);
+
+            // 保存文件到目标路径
+            Files.copy(file.getInputStream(), targetPath);
+
+            return CommonResult.success(fileName, "上传成功");
+        } catch (IOException e) {
+            e.printStackTrace();
+            return CommonResult.success("上传失败");
+        }
+    }
 }

+ 14 - 11
taais-modules/taais-biz/src/main/java/com/taais/biz/domain/bo/Video2imageBo.java

@@ -1,5 +1,6 @@
 package com.taais.biz.domain.bo;
 
+
 import com.taais.biz.domain.Video2image;
 import io.github.linpeilie.annotations.AutoMapper;
 import lombok.Data;
@@ -10,7 +11,7 @@ import com.fasterxml.jackson.annotation.JsonFormat;
 import com.taais.common.orm.core.domain.BaseEntity;
 
 /**
- * 视频转图片 业务对象 video2image
+ * 【请填写功能名称】业务对象 video2image
  *
  * @author km
  * @date 2024-08-16
@@ -22,7 +23,6 @@ public class Video2imageBo extends BaseEntity{
     /**
      * 主键ID
      */
-    @NotNull(message = "主键ID不能为空")
     private Long id;
 
     /**
@@ -34,51 +34,51 @@ public class Video2imageBo extends BaseEntity{
     /**
      * 任务状态
      */
-    @NotBlank(message = "任务状态不能为空")
+    // @NotBlank(message = "任务状态不能为空")
     private String status;
 
     /**
      * 切割好的图片的保存路径
      */
-    @NotBlank(message = "切割好的图片的保存路径不能为空")
+    // @NotBlank(message = "切割好的图片的保存路径不能为空")
     private String outPath;
 
     /**
      * 开始时间
      */
-    @NotNull(message = "开始时间不能为空")
+    // @NotNull(message = "开始时间不能为空")
     @JsonFormat(pattern = "yyyy-MM-dd")
     private Date startTime;
 
     /**
      * 结束时间
      */
-    @NotNull(message = "结束时间不能为空")
+    // @NotNull(message = "结束时间不能为空")
     @JsonFormat(pattern = "yyyy-MM-dd")
     private Date endTime;
 
     /**
      * 耗时
      */
-    @NotNull(message = "耗时不能为空")
+    // @NotNull(message = "耗时不能为空")
     private Long costSecond;
 
     /**
      * 日志
      */
-    @NotBlank(message = "日志不能为空")
+    // @NotBlank(message = "日志不能为空")
     private String log;
 
     /**
      * 备注
      */
-    @NotBlank(message = "备注不能为空")
+    // @NotBlank(message = "备注不能为空")
     private String remarks;
 
     /**
      * 视频本身保存路径
      */
-    @NotBlank(message = "视频本身保存路径不能为空")
+    // @NotBlank(message = "视频本身保存路径不能为空")
     private String path;
 
     /**
@@ -87,5 +87,8 @@ public class Video2imageBo extends BaseEntity{
     @NotNull(message = "切割帧率,默认1不能为空")
     private Long fps;
 
-
+    /**
+     * 视频文件ID
+     */
+    private String fileId;
 }

+ 103 - 0
taais-modules/taais-biz/src/main/java/com/taais/biz/utils/VideoCapture.java

@@ -0,0 +1,103 @@
+/**
+ * @Datetime : 2024-08-17 11:04:20
+ * @Author : WANGKANG
+ * @Email : 1686617586@qq.com
+ * @Blog :
+ * @File : VideoCapture.java
+ * @brief : 视频转图片工具类
+ */
+
+
+package com.taais.biz.utils;
+
+import java.io.IOException;
+
+import org.bytedeco.javacv.FFmpegFrameGrabber;
+
+import java.io.File;
+
+import org.bytedeco.javacv.Frame;
+
+import javax.imageio.ImageIO;
+import java.awt.image.BufferedImage;
+
+import org.bytedeco.javacv.Java2DFrameConverter;
+
+import java.nio.file.Path;
+import java.nio.file.Paths;
+
+public class VideoCapture {
+    /**
+     * 截取视频帧并保存为图片
+     * 设置唯一视频名称,保存到指定的路径下
+     *
+     * @param filePath 视频文件路径
+     * @param savePath 图片保存路径
+     * @param fps      帧率
+     * @throws IOException
+     * @throws InterruptedException
+     */
+    public static void startCaputre(String filePath, String savePath, int fps)
+        throws IOException {
+        FFmpegFrameGrabber grabber = FFmpegFrameGrabber.createDefault(filePath);
+        System.out.println("--- 开始截图 ---");
+        grabber.start();
+        long current_time_stamp = 0;
+        long current_time_stamp_step = 1000000 / fps;
+        int idx = 0;
+        while (true) {
+            Frame frame = grabber.grabImage();
+            if (frame != null) {
+                // System.out.println(frame.timestamp);
+                if (frame.timestamp >= current_time_stamp) {
+                    idx += 1;
+                    current_time_stamp += current_time_stamp_step;
+                    saveImage(frame, savePath, idx);
+                }
+            } else {
+                break;
+            }
+        }
+        grabber.stop();
+        grabber.release();
+        System.out.println("--- 截图完成---");
+    }
+
+    public static boolean saveImage(Frame frame, String savePath, int idx) {
+        try {
+            Path outPath = Paths.get(savePath, System.currentTimeMillis() + "_" + idx + ".jpg");
+            // System.out.println("图片已保存:" + outPath);
+            File outPut = new File(outPath.toString());
+            ImageIO.write(frameToBufferedImage(frame), "jpg", outPut);
+            return true;
+        } catch (IOException e) {
+            e.printStackTrace();
+            return false;
+        }
+    }
+
+    /**
+     * 创建BufferedImage对象
+     */
+    public static BufferedImage frameToBufferedImage(Frame frame) {
+        Java2DFrameConverter converter = new Java2DFrameConverter();
+        BufferedImage bufferedImage = converter.getBufferedImage(frame);
+        converter.close();
+        return bufferedImage;
+    }
+
+    public static int getFramgeByTime(FFmpegFrameGrabber grabber, Long timestamp) throws Exception {
+        grabber.setTimestamp(timestamp, true);
+        Frame frame = grabber.grabFrame(false, true, true, false); // 抓取对应的帧
+        if (frame != null) {
+            return grabber.getFrameNumber();
+        } else {
+            throw new Exception("无法获取对应时间戳的帧");
+        }
+    }
+
+    public static long getTimestampByFrame(FFmpegFrameGrabber grabber, int frame) throws Exception {
+        grabber.setFrameNumber(frame);
+        return grabber.getTimestamp();
+    }
+}