소스 검색

生产端, 截取视频第一帧

wuweihao 2 년 전
부모
커밋
460dafc8dc

+ 6 - 0
720yun_fd_manage/gis_common/src/main/java/com/gis/common/constant/CmdConstant.java

@@ -18,4 +18,10 @@ public class CmdConstant {
      * convert -resize 800x400 /root/user/owen_test/test.jpg /root/user/owen_test/aa.jpg
      * */
     public final static String CONVERT = "convert -resize @size @input @output";
+
+    /**
+     * 截取视频第一帧
+     * ffmpeg -i bb.mp4 -y -vframes 1 -vf scale=100:100/a thumb.jpg
+     */
+    public final static String FFMPEG_FIRST = "ffmpeg -i @input -y -vframes 1 -vf scale=100:100/a @output";
 }

+ 94 - 4
720yun_fd_manage/gis_oss/src/main/java/com/gis/oss/util/FileUtils.java

@@ -4,8 +4,11 @@ import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.img.Img;
 import cn.hutool.core.io.FileUtil;
 import cn.hutool.core.thread.ThreadUtil;
+import cn.hutool.core.util.ReUtil;
 import cn.hutool.core.util.StrUtil;
 import com.gis.common.constant.ConfigConstant;
+import com.gis.common.exception.BaseRuntimeException;
+import com.gis.common.util.DateUtils;
 import com.gis.common.util.ImageUtil;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.lang3.StringUtils;
@@ -23,7 +26,9 @@ import java.io.*;
 import java.net.HttpURLConnection;
 import java.net.URL;
 import java.net.URLDecoder;
+import java.time.LocalDateTime;
 import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Created by owen on 2020/5/12 0012 17:21
@@ -39,6 +44,69 @@ public class FileUtils {
     @Autowired
     ConfigConstant configConstant;
 
+    // 确保同一时间上传文件的唯一性
+    private static final AtomicInteger ATOMIC_INTEGER = new AtomicInteger();
+
+    /**
+     *
+     * @param file
+     * @param isPinYinRename false:时间戳重命名, true:用拼音重名文件
+     * @param savePath 保存地址(前面有斜杠, 后面没有),没有文件名
+     * @return map
+     */
+    public Map<String, Object> uploadMap(MultipartFile file, String savePath, boolean isPinYinRename) {
+        String newName = this.upload(file, savePath, isPinYinRename);
+        HashMap<String, Object> result = new HashMap<>();
+        result.put("fileName", file.getOriginalFilename());
+        result.put("filePath", savePath + "/" + newName);
+        return result;
+
+    }
+
+    /**
+     *
+     * @param file
+     * @param isPinYinRename false:时间戳重命名, true:用拼音重名文件
+     * @param savePath 保存地址(前面有斜杠, 后面没有),没有文件名
+     * @return 文件名
+     */
+    public String upload(MultipartFile file, String savePath, boolean isPinYinRename) {
+
+        // 检查非法文件上传
+        boolean checkFile = this.checkFile(file);
+        if (!checkFile) {
+            throw new BaseRuntimeException("上传文件格式有误, 请重新上传");
+        }
+
+
+        // 文件目录
+        String fileName = file.getOriginalFilename();
+        String newName;
+        if (isPinYinRename){
+            newName = RegexUtil.getPinyinName(fileName);
+        } else {
+            String suffix = StringUtils.substringAfterLast(fileName, ".");
+            newName =  this.getDateTime() + ATOMIC_INTEGER.incrementAndGet() + "." + suffix;
+        }
+
+        savePath = configConstant.serverBasePath + savePath + "/" + newName;
+        log.info("保存文件地址:{}", savePath);
+
+        try {
+            FileUtil.writeFromStream(file.getInputStream(), savePath);
+
+            return newName;
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+
+    private static String getDateTime(){
+        return DateUtil.format(LocalDateTime.now(), "YYYYMMDD_HHMMSSSSS");
+    }
+
 
     /**
      * 多文件上传
@@ -761,14 +829,36 @@ public class FileUtils {
      * 真删除文件
      * @param path 参数是相对地址
      */
+//    public void del(String path){
+//        if (StrUtil.isNotBlank(path)){
+//            String delPath = configConstant.serverBasePath + path;
+//            FileUtil.del(delPath);
+//            log.info("真删除文件: {}", delPath);
+//        }
+//    }
+
+    /**
+     * 真删除文件
+     * @param path 参数是相对地址
+     */
     public void del(String path){
-        if (StrUtil.isNotBlank(path)){
-            String delPath = configConstant.serverBasePath + path;
-            FileUtil.del(delPath);
-            log.info("真删除文件: {}", delPath);
+        if (StrUtil.isBlank(path)){
+            return;
+        }
+        log.info("path: {}", path);
+        // 过滤 路径为/////, 后面必须跟非斜杠字符
+        String regex = "^[/|\\\\]{1,2}[^/|\\\\]\\S";
+        if (!ReUtil.contains(regex, path)){
+            log.warn("非法路径删除:{}", path);
+            return;
         }
+
+        String delPath = configConstant.serverBasePath + path;
+        FileUtil.del(delPath);
+        log.info("真删除文件: {}", delPath);
     }
 
 
 
+
 }

+ 56 - 0
720yun_fd_manage/gis_oss/src/main/java/com/gis/oss/util/RegexUtil.java

@@ -0,0 +1,56 @@
+package com.gis.oss.util;
+
+import cn.hutool.extra.pinyin.PinyinUtil;
+import org.apache.commons.lang3.StringUtils;
+
+public class RegexUtil {
+
+    // 需要过滤的特殊字符
+    static String [] specialSql = {"%","or","=","and"};
+
+    /** 处理特殊符号,变空值*/
+    public static String specificSymbol(String str){
+
+        String regEx = "[\\s`~!@#$%^&*()+=|{}':;\\[\\]<>/?·~!@#¥%……&*()——+|{}【】‘;:“”。,、?]";
+        return str.replaceAll(regEx, "");
+    }
+
+
+    /** 中文转拼音*/
+    public static String getPinyinName(String str){
+        // 去除特殊符号
+        String pinyinName = RegexUtil.specificSymbol(str);
+        pinyinName = PinyinUtil.getPinyin(pinyinName, "");
+        // 转小写
+//        pinyinName =  StringUtils.lowerCase(pinyinName);
+        return pinyinName;
+
+    }
+
+
+    /**
+     * sql 过滤特殊字符
+     * @param str
+     * @return
+     */
+    public static String sqlReplaceSpecialStr(String str){
+        str = StringUtils.trim(str);
+        str = str.toLowerCase();
+        for (String s : specialSql) {
+            if (str.contains(s)) {
+                str = str.replaceAll(s, "");
+            }
+        }
+        return str;
+    }
+
+
+
+    public static void main(String[] args) {
+        String str = "我·是 中—国(人), 你-在{干嘛}--哈—哈。 ddd.jpg";
+        System.out.println(specificSymbol(str));
+    }
+
+}
+
+

+ 78 - 9
720yun_fd_manage/gis_service/src/main/java/com/gis/service/impl/FodderServiceImpl.java

@@ -32,6 +32,7 @@ import javax.transaction.Transactional;
 import java.util.Arrays;
 import java.util.Date;
 import java.util.List;
+import java.util.Map;
 
 
 /**
@@ -64,6 +65,8 @@ public class FodderServiceImpl extends IBaseServiceImpl<FodderEntity, Long> impl
     @Autowired
     FileAndOssUtil fileAndOssUtil;
 
+
+
     // 用户最大空间3G
     static int MAX_SIZE = 1024 * 1024 * 3;
 
@@ -172,16 +175,10 @@ public class FodderServiceImpl extends IBaseServiceImpl<FodderEntity, Long> impl
                     } else {
                         iconPath = ossUrl;
                     }
-
-                    // 2022-11-02 素材库缩略图不需要压缩, 前端使用oss截取缩略图。 当图片过大自定义logo时, 生成二维码会变慢
-//                    iconPath = ossUrl;
-
-                    // todo 这个是很耗新能的,如果新能有问题,改用前端
-                    dpi = ImageUtil.dpi(file);
-
+                } else if ("video".equals(type)){
+                    iconPath = createThumb(type, file);
                 }
                 // 普通素材直接上传oss, 服务器不保留文件
-//                aliyunOssUtil.upload(file.getBytes(), ossPath);
                 fileAndOssUtil.upload(file.getBytes(), ossPath);
 
 
@@ -197,7 +194,7 @@ public class FodderServiceImpl extends IBaseServiceImpl<FodderEntity, Long> impl
             entity.setIcon(iconPath);
             entity.setUserId(phone);
             entity.setFileSize(size + "");
-            entity.setDpi(dpi);
+            entity.setDpi(getDpi(file, type));
             entity.setPreviewIcon(ossPreviewIcon);
             entity.setTempId(tempId);
 
@@ -268,6 +265,27 @@ public class FodderServiceImpl extends IBaseServiceImpl<FodderEntity, Long> impl
     }
 
 
+    public String convertImg(String inputFilePath, String outPath, Integer width, Integer height, String imgName) {
+
+        // 使用convert压缩图片
+        String cmd = CmdConstant.CONVERT;
+        String size = width + "x" + height;
+        cmd = cmd.replace("@size", size);
+        cmd = cmd.replace("@input", inputFilePath);
+        cmd = cmd.replace("@output", outPath);
+
+        // 开始压缩
+        CmdUtils.callShell(cmd);
+
+
+        if (!FileUtil.isFile(outPath)) {
+            log.error("缩略图不存在: " + outPath);
+        }
+
+        return outPath;
+    }
+
+
     @Override
     public Result search(FodderPageDto param) {
         startPage(param);
@@ -362,5 +380,56 @@ public class FodderServiceImpl extends IBaseServiceImpl<FodderEntity, Long> impl
         BaseRuntimeException.isTrue(size >= MAX_SIZE, ErrorEnum.FAILURE_CODE_3024.code(), "该用户空间已满");
     }
 
+    /**
+     *
+     * @param type 文件类型
+     * @param file 文件流
+     * @return
+     */
+    private String createThumb(String type, MultipartFile file){
+
+        // 写入文件
+        String time = DateUtil.format(new Date(), "yyyyMMdd_HHmmssSSS");
+        String dir = "/temp/" + time;
+        Map<String, Object> uploadMap = fileUtils.uploadMap(file, dir, false);
+        String filePath = uploadMap.get("filePath").toString();
+        filePath = configConstant.serverBasePath + filePath;
+        String thumbName = dir + "/thumb.jpg";
+        String thumbPath = configConstant.serverBasePath + thumbName;
+
+        // 缩略图
+        String ossKey = configConstant.ossBasePath + "/fodder/thumb/thumb_" + time + ".jpg";
+        String iconPath = configConstant.ossDomain + ossKey;
+
+        // asw 环境需要使用ffmpeg 截图视频第一帧
+        if (configConstant.active.contains("Aws") && "video".equals(type)){
+            log.info("aws 截取视频第一帧");
+            String cmd = CmdConstant.FFMPEG_FIRST;
+            cmd = cmd.replaceAll("@inPath", filePath);
+            cmd = cmd.replaceAll("@outPath", thumbPath);
+            CmdUtils.callShell(cmd);
+            BaseRuntimeException.isTrue(!FileUtil.isFile(thumbPath), null, "截取视频第一帧失败");
+        }
+
+        fileAndOssUtil.upload(thumbPath, ossKey);
+//        fileUtils.del(dir);
+
+        return iconPath;
+    }
+
+    /**
+     * 获取图片dpi
+     * 这个是很耗性能的,如果性能有问题,改用前端
+     * @param file
+     * @param type
+     * @return
+     */
+    private String getDpi(MultipartFile file, String type){
+        if (!"image".equals(type)){
+            return null;
+        }
+        return ImageUtil.dpi(file);
+    }
+
 
 }

+ 4 - 13
720yun_fd_manage/remark-m.md

@@ -38,6 +38,7 @@
 5. krpano 切全景图 
 6. convert 切缩略图
 7. rabbit MQ
+8. ffmpeg aws环境用来 截取视频第一帧
 
 #dev    
  ## rabbit MQ
@@ -108,21 +109,11 @@
  
 
  
+## doc
+    aws: https://docs.aws.amazon.com/zh_cn/lambda/latest/dg/with-s3-tutorial.html
  
- # 错误码汇总:
- 0: 请求成功
- -1: 请求失败
- 500: 服务器异常
- 2001: 对象不存在
- 3005: 当前无操作权限
- 3020: 文件不能为空
- 3021: 非常文件
- 3022:上传文件超过最大值
- 5001: token为空
- 5004: 密码有误
  
- 
- # appId: 
+# appId: 
     appId :BDA385EC848C1A425F746869011C8D23, 四维看看