Prechádzať zdrojové kódy

feat: image compress async

lihao16 7 mesiacov pred
rodič
commit
9958463c81

+ 1 - 1
smsb-modules/smsb-device/pom.xml

@@ -117,7 +117,7 @@
         <dependency>
             <groupId>com.aizuda</groupId>
             <artifactId>zlm4j</artifactId>
-            <version>1.2.2</version>
+            <version>1.2.5</version>
         </dependency>
 
         <!-- 阿里JSON解析器 -->

+ 2 - 2
smsb-modules/smsb-netty/src/main/java/com/inspur/netty/server/NettyServer.java

@@ -63,9 +63,9 @@ public class NettyServer {
                             channel.pipeline().addLast(new SourcePlayRecordHandler());
                         }
                     });
-            ChannelFuture f = b.bind(8900).sync();
+            ChannelFuture f = b.bind(8990).sync();
             if (f.isSuccess()) {
-                log.info("Server,启动Netty服务端成功,端口号:" + 8900);
+                log.info("Server,启动Netty服务端成功,端口号:" + 8990);
             }
         } catch (Exception e) {
             e.printStackTrace();

+ 28 - 0
smsb-modules/smsb-source/src/main/java/com/inspur/source/controller/SmsbMinioDataController.java

@@ -1,6 +1,7 @@
 package com.inspur.source.controller;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.util.ObjectUtil;
 import com.inspur.device.domain.vo.DeviceAlarmNumTopVo;
 import com.inspur.source.domain.bo.SmsbMinioDataBo;
 import com.inspur.source.domain.vo.DashBoardPlayVo;
@@ -22,8 +23,12 @@ import org.dromara.common.log.enums.BusinessType;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.web.core.BaseController;
+import org.dromara.system.domain.vo.SysOssUploadVo;
+import org.dromara.system.domain.vo.SysOssVo;
+import org.springframework.http.MediaType;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
 
@@ -75,6 +80,29 @@ public class SmsbMinioDataController extends BaseController {
         return R.ok(smsbMinioDataService.queryById(id));
     }
 
+    /**
+     * 上传OSS对象存储
+     *
+     * @param file 文件
+     */
+    @Log(title = "OSS对象存储", businessType = BusinessType.INSERT)
+    @PostMapping(value = "/upload/smsb", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
+    public R<SysOssUploadVo> uploadSmsb(@RequestPart("file") MultipartFile file) {
+        if (ObjectUtil.isNull(file)) {
+            return R.fail("上传文件不能为空");
+        }
+        SysOssVo oss = smsbMinioDataService.uploadSmsb(file);
+        if (null == oss) {
+            return R.fail("上传失败,已存在相同文件");
+        }
+        SysOssUploadVo uploadVo = new SysOssUploadVo();
+        uploadVo.setUrl(oss.getUrl());
+        uploadVo.setFileName(oss.getOriginalName());
+        uploadVo.setOssId(oss.getOssId().toString());
+        return R.ok(uploadVo);
+    }
+
+
     /**
      * 新增文件资源
      */

+ 2 - 0
smsb-modules/smsb-source/src/main/java/com/inspur/source/mapper/SmsbMinioTransRecordMapper.java

@@ -2,6 +2,7 @@ package com.inspur.source.mapper;
 
 import com.inspur.source.domain.SmsbMinioTransRecord;
 import com.inspur.source.domain.vo.SmsbMinioTransRecordVo;
+import org.apache.ibatis.annotations.Param;
 import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
 
 /**
@@ -12,4 +13,5 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
  */
 public interface SmsbMinioTransRecordMapper extends BaseMapperPlus<SmsbMinioTransRecord, SmsbMinioTransRecordVo> {
 
+    void updateByFileId(@Param("fileId") Long id, @Param("transProgress") Integer transProgress, @Param("transResult") Integer transResult);
 }

+ 9 - 0
smsb-modules/smsb-source/src/main/java/com/inspur/source/service/ISmsbMinioDataService.java

@@ -7,6 +7,8 @@ import com.inspur.source.domain.vo.FileStatisticsVo;
 import com.inspur.source.domain.vo.SmsbMinioDataVo;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
+import org.dromara.system.domain.vo.SysOssVo;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.util.Collection;
 import java.util.List;
@@ -96,4 +98,11 @@ public interface ISmsbMinioDataService {
      * @return
      */
     DiskUseVo diskUse();
+
+    /**
+     * 上传文件
+     * @param file
+     * @return
+     */
+    SysOssVo uploadSmsb(MultipartFile file);
 }

+ 349 - 57
smsb-modules/smsb-source/src/main/java/com/inspur/source/service/impl/SmsbMinioDataServiceImpl.java

@@ -2,6 +2,7 @@ package com.inspur.source.service.impl;
 
 import cn.hutool.core.collection.CollectionUtil;
 import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
@@ -13,33 +14,41 @@ import com.inspur.source.mapper.*;
 import com.inspur.source.service.ISmsbMinioDataService;
 import lombok.RequiredArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.dromara.common.core.exception.ServiceException;
 import org.dromara.common.core.utils.MapstructUtils;
 import org.dromara.common.core.utils.StringUtils;
 import org.dromara.common.mybatis.core.page.PageQuery;
 import org.dromara.common.mybatis.core.page.TableDataInfo;
 import org.dromara.common.oss.core.OssClient;
 import org.dromara.common.oss.entity.UploadResult;
+import org.dromara.common.oss.enumd.AccessPolicyType;
 import org.dromara.common.oss.factory.OssFactory;
+import org.dromara.common.redis.utils.RedisUtils;
 import org.dromara.common.satoken.utils.LoginHelper;
 import org.dromara.system.domain.SysOss;
 import org.dromara.system.domain.vo.SysOssVo;
 import org.dromara.system.mapper.SysOssMapper;
+import org.jetbrains.annotations.NotNull;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.imageio.ImageIO;
 import java.awt.*;
 import java.awt.image.BufferedImage;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
+import java.io.*;
 import java.math.BigDecimal;
 import java.net.URL;
+import java.nio.charset.StandardCharsets;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.time.Duration;
 import java.util.List;
 import java.util.*;
+import java.util.concurrent.*;
 import java.util.stream.Collectors;
 
 /**
@@ -67,6 +76,10 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
     @Value("${server.tempDir}")
     private String tempDir;
 
+    private static final String SOURCE_FILE_COMPRESS_STATUS = "global:compress:status:";
+
+    // 30分钟
+    private static final long TIMEOUT = 30 * 60 * 1000;
     /**
      * 查询文件资源
      *
@@ -150,7 +163,7 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
      * @return 是否新增成功
      */
     @Override
-    @Transactional(rollbackFor = Exception.class)
+    // @Transactional(rollbackFor = Exception.class)
     public Boolean insertByBo(SmsbMinioDataBo bo) {
         SmsbMinioData add = MapstructUtils.convert(bo, SmsbMinioData.class);
         // 根据ossId 获取文件详细信息
@@ -162,69 +175,122 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
             return false;
         }
         List<Long> sourceTreeIds = bo.getSourceTreeIds();
-        List<SmsbMinioTransRecord> transRecordList = new ArrayList<>();
+        List<SmsbMinioData> minioDataList = new ArrayList<>();
         for (String ossId : ossIdList) {
             SysOssVo sysOssVo = sysOssMapper.selectVoById(ossId);
-            // 创建add
-            createSaveMinioData(sysOssVo, add);
-            boolean flag = baseMapper.insert(add) > 0;
+            // 创建minioData
+            SmsbMinioData minioData = createSaveMinioData(sysOssVo);
+            boolean flag = baseMapper.insert(minioData) > 0;
             if (flag) {
-                bo.setId(add.getId());
+                bo.setId(minioData.getId());
                 // 保存sourceTree-source关联关系
                 if (CollectionUtil.isNotEmpty(sourceTreeIds)) {
                     sourceTreeIds.forEach(sourceTreeId -> {
                         SmsbSourceTreeRel sourceTreeRel = new SmsbSourceTreeRel();
-                        sourceTreeRel.setSourceId(add.getId());
+                        sourceTreeRel.setSourceId(minioData.getId());
                         sourceTreeRel.setTreeId(sourceTreeId);
                         smsbSourceTreeRelMapper.insert(sourceTreeRel);
                     });
                 } else {
                     SmsbSourceTreeRel sourceTreeRel = new SmsbSourceTreeRel();
-                    sourceTreeRel.setSourceId(add.getId());
+                    sourceTreeRel.setSourceId(minioData.getId());
                     sourceTreeRel.setTreeId(0L);
                     smsbSourceTreeRelMapper.insert(sourceTreeRel);
                 }
                 // 创建转码任务
-                SmsbMinioTransRecord transRecord = createTransRecord(add);
-                transRecordList.add(transRecord);
+                SmsbMinioTransRecord transRecord = createTransRecord(minioData);
+                minioDataList.add(minioData);
             }
         }
-        doMinioFileTrans(transRecordList, baseMapper, transRecordMapper);
+        // updateFileTransResult(minioDataList, baseMapper, transRecordMapper);
+        CompletableFuture.runAsync(() -> updateFileTransResult(minioDataList, baseMapper, transRecordMapper));
         return true;
     }
 
-    private void doMinioFileTrans(List<SmsbMinioTransRecord> transRecordList, SmsbMinioDataMapper baseMapper, SmsbMinioTransRecordMapper transRecordMapper) {
+    private void updateFileTransResult(List<SmsbMinioData> minioDataList, SmsbMinioDataMapper baseMapper, SmsbMinioTransRecordMapper transRecordMapper) {
+        ScheduledExecutorService timeoutExecutor = Executors.newScheduledThreadPool(1);
         Runnable transTask = () -> {
-            for (SmsbMinioTransRecord transRecord : transRecordList) {
-                log.info("doMinioFileTrans transRecord : " + transRecord.toString());
-                SmsbMinioData smsbMinioData = baseMapper.selectById(transRecord.getFileId());
-                log.info("doMinioFileTrans smsbMinioData : " + smsbMinioData.toString());
-                // 图片转码
-                // 更新转码记录状态 正在转
-                transRecord.setTransProgress(1);
-                transRecordMapper.updateById(transRecord);
-                if (transRecord.getTransType() == 1) {
-                    log.info("transTask start : " + smsbMinioData.getId() + ",file Type is image");
-                    compressImg(smsbMinioData, transRecord);
-                } else if (transRecord.getTransType() == 2) {
-                    // TODO 视频转码
+            int cycleTimes = minioDataList.size();
+                while (true) {
+                    if (cycleTimes <= 0) {
+                        log.info("updateFileTransResult cycleTimes <= 0 break task is end");
+                        break;
+                    }
+                    for (SmsbMinioData minioData : minioDataList) {
+                        try {
+                            // 等待5秒
+                            Thread.sleep(5000);
+                        } catch (InterruptedException e) {
+                            throw new RuntimeException(e);
+                        }
+                        String redisKey = SOURCE_FILE_COMPRESS_STATUS + minioData.getValue();
+                        Integer transProgress = 0;
+                        Integer transResult = 0;
+                        String redisValue = RedisUtils.getCacheObject(redisKey);
+                        if (StringUtils.isEmpty(redisValue)) {
+                            log.info("redis key get value is empty,redisKey : " + redisKey);
+                            continue;
+                        }
+                        if ("transing".equals(redisValue)) {
+                            log.info("redis key get value is transing,redisKey : " + redisKey);
+                            // 更新转码记录根据fileId状态
+                            transProgress = 1;
+                            transRecordMapper.updateByFileId(minioData.getId(),transProgress,transResult);
+                            continue;
+                        }
+                        if (redisValue.startsWith("success_")) {
+                            log.info("redis key get value is success,redisKey : " + redisKey + ",redis value : " + redisValue);
+                            String imageUrl = redisValue.replace("success_","");
+                            transProgress = 2;
+                            transResult = 1;
+                            transRecordMapper.updateByFileId(minioData.getId(),transProgress,transResult);
+                            minioData.setTransState(1);
+                            minioData.setScreenshot(imageUrl);
+                            baseMapper.updateById(minioData);
+                            RedisUtils.deleteObject(redisKey);
+                            cycleTimes--;
+                            continue;
+                        }
+                        if ("fail".equals(redisValue)) {
+                            log.info("redis key get value is fail,redisKey : " + redisKey);
+                            transProgress = 2;
+                            transResult = 2;
+                            transRecordMapper.updateByFileId(minioData.getId(),transProgress,transResult);
+                            minioData.setTransState(2);
+                            baseMapper.updateById(minioData);
+                            RedisUtils.deleteObject(redisKey);
+                            cycleTimes--;
+                        }
+                    }
                 }
-                // 更新转码记录状态
-                baseMapper.updateById(smsbMinioData);
-                transRecordMapper.updateById(transRecord);
-                log.info("transTask end : " + smsbMinioData.getId() + ",trans result : " + smsbMinioData.getTransState());
-            }
         };
         // 提交Runnable任务到线程池
-        threadPoolTaskExecutor.submit(transTask);
+        Future<?> future = threadPoolTaskExecutor.submit(transTask);
+        timeoutExecutor.schedule(() -> {
+            if (!future.isDone()) {
+                log.info("updateFileTransResult Task is taking too long, terminating...");
+                // 强制终止任务
+                future.cancel(true);
+            }
+        }, TIMEOUT, TimeUnit.MILLISECONDS);
+        // 等待任务完成或超时
+        try {
+            future.get();
+        } catch (InterruptedException | ExecutionException e) {
+            log.error("updateFileTransResult Task execution failed", e);
+        } finally {
+            future.cancel(true);
+            timeoutExecutor.shutdown();
+        }
     }
 
-    public void compressImg(SmsbMinioData smsbMinioData, SmsbMinioTransRecord transRecord) {
+    public boolean compressImg(SysOssVo sysOssVo, MultipartFile file, String redisKey) {
+        RedisUtils.setCacheObject(redisKey, "transing", Duration.ofMinutes(30));
         // 图片的横屏、竖屏
-        String hwType = getHWType(smsbMinioData);
+        String hwType = getHWType(sysOssVo);
         // 压缩前分辨率
-        Integer width = Integer.parseInt(smsbMinioData.getResolution().split("x")[0]);
-        Integer height = Integer.parseInt(smsbMinioData.getResolution().split("x")[1]);
+        Integer width = Integer.parseInt(sysOssVo.getResolution().split("x")[0]);
+        Integer height = Integer.parseInt(sysOssVo.getResolution().split("x")[1]);
         // 缩放比率因子,保留2位小数
         double factor = 0.0;
         // 压缩后分辨率
@@ -243,25 +309,18 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
         }
         log.info("image hwType is : " + hwType + ",imageWidth : " + imageWidth + ",imageHeight : " + imageHeight);
         //  图片压缩
-        boolean compressResult = doCompressImg(imageWidth, imageHeight, smsbMinioData);
-        // 压缩成功 更新主表和记录表状态
-        if (compressResult) {
-            smsbMinioData.setTransState(1);
-            transRecord.setResult(1);
-        } else {
-            smsbMinioData.setTransState(2);
-            transRecord.setResult(2);
-        }
-        transRecord.setTransProgress(2);
+        boolean compressResult = doCompressImg(imageWidth, imageHeight, redisKey, file);
+        return compressResult;
     }
 
-    private boolean doCompressImg(double imageWidth, double imageHeight, SmsbMinioData smsbMinioData) {
+    private boolean doCompressImg(double imageWidth, double imageHeight,String redisKey, MultipartFile file) {
         // 临时文件
         String tempFile = tempDir + File.separator + System.currentTimeMillis() + ".png";
+        File imageFile = null;
         try {
-            URL srcFileUrl = new URL(smsbMinioData.getFileUrl());
+            imageFile = convertMultipartFileToFile(file);
             //获取图片对象
-            Image src = javax.imageio.ImageIO.read(srcFileUrl);
+            Image src = javax.imageio.ImageIO.read(imageFile);
             //缓存图片对象
             BufferedImage to = new BufferedImage((int) imageWidth, (int) imageHeight,
                 BufferedImage.TYPE_INT_RGB);
@@ -279,15 +338,18 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
             compressImageSize(tempFile);
             // 上传文件至minio
             String imageUrl = uploadCompressImage(tempFile);
-            // 压缩成功,报错压缩图片路径
-            smsbMinioData.setScreenshot(imageUrl);
+            RedisUtils.setCacheObject(redisKey, "success_" + imageUrl, Duration.ofMinutes(10));
             return true;
         } catch (IOException ex) {
             log.error("doCompressImg exception : " + ex.getMessage());
+            RedisUtils.setCacheObject(redisKey, "fail", Duration.ofMinutes(10));
             return false;
         } finally {
             // 文件删除
             deleteFiles(tempFile);
+            if (imageFile != null) {
+                imageFile.delete();
+            }
         }
     }
 
@@ -333,9 +395,9 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
     }
 
 
-    private String getHWType(SmsbMinioData smsbMinioData) {
-        Integer width = Integer.parseInt(smsbMinioData.getResolution().split("x")[0]);
-        Integer height = Integer.parseInt(smsbMinioData.getResolution().split("x")[1]);
+    private String getHWType(SysOssVo sysOssVo) {
+        Integer width = Integer.parseInt(sysOssVo.getResolution().split("x")[0]);
+        Integer height = Integer.parseInt(sysOssVo.getResolution().split("x")[1]);
         if (width >= height) {
             return "width";
         } else {
@@ -355,7 +417,8 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
         return transRecord;
     }
 
-    private void createSaveMinioData(SysOssVo sysOssVo, SmsbMinioData add) {
+    private SmsbMinioData createSaveMinioData(SysOssVo sysOssVo) {
+        SmsbMinioData add = new SmsbMinioData();
         add.setId(null);
         add.setDelFlag(0);
         add.setKeyName(sysOssVo.getFileName());
@@ -374,6 +437,7 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
         // validEntityBeforeSave(add);
         add.setValue(sysOssVo.getOssId().toString());
         add.setCreateUser(LoginHelper.getLoginUser().getUsername());
+        return add;
     }
 
     /**
@@ -474,4 +538,232 @@ public class SmsbMinioDataServiceImpl implements ISmsbMinioDataService {
         result.setFree(total - result.getUsed());
         return result;
     }
+
+    /**
+     * 上传 MultipartFile 到对象存储服务,并保存文件信息到数据库
+     *
+     * @param file 要上传的 MultipartFile 对象
+     * @return 上传成功后的 SysOssVo 对象,包含文件信息
+     * @throws ServiceException 如果上传过程中发生异常,则抛出 ServiceException 异常
+     */
+    @Override
+    public SysOssVo uploadSmsb(MultipartFile file) {
+        // 计算文件的MD5
+        String md5 = getFileMD5(file);
+        // 根据Md5查询当前租户下是否存在相同文件
+        SysOssVo sysOss = sysOssMapper.selectVoOne(new LambdaQueryWrapper<SysOss>().eq(SysOss::getFileMd5, md5));
+        if (sysOss != null) {
+            return null;
+        }
+        String originalfileName = file.getOriginalFilename();
+        String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
+        OssClient storage = OssFactory.instance();
+        UploadResult uploadResult;
+        try {
+            uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
+        } catch (IOException e) {
+            throw new ServiceException(e.getMessage());
+        }
+        // 保存文件信息
+        return buildResultEntityV2(originalfileName, suffix, storage.getConfigKey(), uploadResult, md5, file);
+    }
+
+    private String getFileMD5(MultipartFile file) {
+        try (InputStream inputStream = file.getInputStream()) {
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            byte[] buffer = new byte[1024];
+            int bytesRead;
+
+            while ((bytesRead = inputStream.read(buffer)) != -1) {
+                md.update(buffer, 0, bytesRead);
+            }
+
+            byte[] md5Bytes = md.digest();
+            StringBuilder hexString = new StringBuilder();
+            for (byte b : md5Bytes) {
+                hexString.append(String.format("%02x", b));
+            }
+            return hexString.toString();
+        } catch (IOException | NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            return null;
+        }
+    }
+
+    @NotNull
+    private SysOssVo buildResultEntityV2(String originalfileName, String suffix, String configKey, UploadResult uploadResult, String md5, MultipartFile file) {
+        SysOss oss = new SysOss();
+        oss.setUrl(uploadResult.getUrl());
+        oss.setFileSuffix(suffix);
+        oss.setFileName(uploadResult.getFilename());
+        oss.setOriginalName(originalfileName);
+        oss.setService(configKey);
+        oss.setFileMd5(md5);
+        getFileInfo(oss, file);
+        sysOssMapper.insert(oss);
+        SysOssVo sysOssVo = MapstructUtils.convert(oss, SysOssVo.class);
+        sysOssVo = matchingUrl(sysOssVo);
+        compressTask(sysOssVo, file);
+        return sysOssVo;
+    }
+
+    private void compressTask(SysOssVo sysOssVo, MultipartFile file) {
+        // 启动线程开启转码压缩
+        Runnable transTask = () -> {
+            log.info("doMinioFileTrans sysOssVo : " + sysOssVo.toString());
+            // 缓存ossId
+            String redisKey = SOURCE_FILE_COMPRESS_STATUS + sysOssVo.getOssId().toString();
+            RedisUtils.setCacheObject(redisKey, "waiting", Duration.ofMinutes(30));
+            boolean compressResult = false;
+            if (sysOssVo.getFileType() == 1) {
+                log.info("transTask start : " + sysOssVo.getOssId() + ",file Type is image");
+                compressResult = compressImg(sysOssVo, file, redisKey);
+            } else if (sysOssVo.getFileType() == 2) {
+                // TODO 视频转码
+            }
+            log.info("transTask end : " + sysOssVo.getOssId() + ",trans result : " + compressResult);
+
+        };
+        // 提交Runnable任务到线程池
+        threadPoolTaskExecutor.submit(transTask);
+    }
+
+    /**
+     * 桶类型为 private 的URL 修改为临时URL时长为120s
+     *
+     * @param oss OSS对象
+     * @return oss 匹配Url的OSS对象
+     */
+    private SysOssVo matchingUrl(SysOssVo oss) {
+        OssClient storage = OssFactory.instance(oss.getService());
+        // 仅修改桶类型为 private 的URL,临时URL时长为120s
+        if (AccessPolicyType.PRIVATE == storage.getAccessPolicy()) {
+            oss.setUrl(storage.getPrivateUrl(oss.getFileName(), 120));
+        }
+        return oss;
+    }
+
+    private void getFileInfo(SysOss oss, MultipartFile file) {
+        // 文件大小
+        oss.setFileSize((int) (file.getSize() / 1024));
+        // 分辨率
+        String resolution = "0x0";
+        // 视频时长
+        Integer duration = 0;
+        // 视频码率
+        Integer codeRate = 0;
+        // 文件类型
+        Integer fileType = isImage(file) ? 1 : 2;
+        oss.setFileType(fileType);
+        if (fileType == 1) {
+            resolution = getImageResolution(file);
+        }
+        if (fileType == 2) {
+            String videoInfo = getVideoInfo(file);
+            if (StringUtils.isEmpty(videoInfo)) {
+                return;
+            }
+            resolution = videoInfo.split(",")[0].trim();
+            codeRate = Integer.parseInt(videoInfo.split(",")[1].trim()) / 1000;
+            duration = Integer.parseInt(videoInfo.split(",")[2].trim());
+        }
+        oss.setResolution(resolution);
+        oss.setCodeRate(codeRate);
+        oss.setDuration(duration);
+    }
+
+    /**
+     * 获取图片的分辨率(宽度和高度)
+     *
+     * @param file 图片文件(MultipartFile)
+     * @return 返回图片的宽度和高度,例如 "1920x1080",如果失败返回 null
+     */
+    public static String getImageResolution(MultipartFile file) {
+        try {
+            BufferedImage image = ImageIO.read(file.getInputStream());
+            if (image != null) {
+                int width = image.getWidth();
+                int height = image.getHeight();
+                // 返回格式:1920x1080
+                return width + "x" + height;
+            }
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        // 读取失败
+        return null;
+    }
+
+    /**
+     * 判断文件是否是图片
+     *
+     * @param file 上传的文件
+     * @return true:是图片,false:不是
+     */
+    public static boolean isImage(MultipartFile file) {
+        String contentType = file.getContentType();
+        return contentType != null && contentType.startsWith("image/");
+    }
+
+    /**
+     * 获取视频的分辨率、码率和时长
+     *
+     * @param file MultipartFile 视频文件
+     * @return 返回视频信息,例如 "1920x1080, 1500kbps, 120.5s",如果失败返回 null。
+     */
+    public static String getVideoInfo(MultipartFile file) {
+        // 将 MultipartFile 转为临时文件
+        File tempFile = convertMultipartFileToFile(file);
+        if (tempFile == null) {
+            return null;
+        }
+        try {
+            // 执行 ffprobe 命令
+            ProcessBuilder pb = new ProcessBuilder(
+                "ffprobe",
+                "-v", "error",
+                "-select_streams", "v:0",
+                "-show_entries", "stream=width,height,bit_rate,duration",
+                "-of", "csv=p=0",
+                tempFile.getAbsolutePath()
+            );
+            Process process = pb.start();
+            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
+            String videoInfo = reader.readLine();
+            process.waitFor();
+            tempFile.delete(); // 删除临时文件
+
+            if (videoInfo != null) {
+                // 解析返回数据
+                String[] values = videoInfo.split(",");
+                if (values.length >= 4) {
+                    // 分辨率
+                    String resolution = values[0] + "x" + values[1];
+                    // 视频时长
+                    // String duration = String.format("%.2f", Double.parseDouble(values[3])) + "s";
+                    String duration = values[2].split("\\.")[0];
+                    // 码率 (转换为 kbps)
+                    String bitrate = values[3].split("\\.")[0];
+                    return resolution + ", " + bitrate + ", " + duration;
+                }
+            }
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
+
+    /**
+     * MultipartFile 转 File
+     */
+    private static File convertMultipartFileToFile(MultipartFile file) {
+        try {
+            File tempFile = File.createTempFile("temp_", ".tmp");
+            file.transferTo(tempFile);
+            return tempFile;
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
 }

+ 3 - 0
smsb-modules/smsb-source/src/main/resources/mapper/SmsbMinioTransRecordMapper.xml

@@ -4,4 +4,7 @@
     "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.inspur.source.mapper.SmsbMinioTransRecordMapper">
 
+    <update id="updateByFileId">
+        update smsb_minio_trans_record set trans_progress = #{transProgress} , result = #{transResult} where file_id = #{fileId}
+    </update>
 </mapper>

+ 1 - 21
smsb-modules/smsb-system/src/main/java/org/dromara/system/controller/system/SysOssController.java

@@ -81,27 +81,7 @@ public class SysOssController extends BaseController {
         return R.ok(uploadVo);
     }
 
-    /**
-     * 上传OSS对象存储
-     *
-     * @param file 文件
-     */
-    @Log(title = "OSS对象存储", businessType = BusinessType.INSERT)
-    @PostMapping(value = "/upload/smsb", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
-    public R<SysOssUploadVo> uploadSmsb(@RequestPart("file") MultipartFile file) {
-        if (ObjectUtil.isNull(file)) {
-            return R.fail("上传文件不能为空");
-        }
-        SysOssVo oss = ossService.uploadSmsb(file);
-        if (null == oss) {
-            return R.fail("上传失败,已存在相同文件");
-        }
-        SysOssUploadVo uploadVo = new SysOssUploadVo();
-        uploadVo.setUrl(oss.getUrl());
-        uploadVo.setFileName(oss.getOriginalName());
-        uploadVo.setOssId(oss.getOssId().toString());
-        return R.ok(uploadVo);
-    }
+
 
     /**
      * 上传OSS OTA 文件

+ 2 - 0
smsb-modules/smsb-system/src/main/java/org/dromara/system/domain/vo/SysOssVo.java

@@ -1,5 +1,6 @@
 package org.dromara.system.domain.vo;
 
+import lombok.ToString;
 import org.dromara.common.translation.annotation.Translation;
 import org.dromara.common.translation.constant.TransConstant;
 import org.dromara.system.domain.SysOss;
@@ -17,6 +18,7 @@ import java.util.Date;
  */
 @Data
 @AutoMapper(target = SysOss.class)
+@ToString
 public class SysOssVo implements Serializable {
 
     @Serial

+ 0 - 7
smsb-modules/smsb-system/src/main/java/org/dromara/system/service/ISysOssService.java

@@ -77,13 +77,6 @@ public interface ISysOssService {
      */
     Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
 
-    /**
-     * smsb upload file
-     * @param file
-     * @return
-     */
-    SysOssVo uploadSmsb(MultipartFile file);
-
     /**
      * smsb upload ota file
      * @param file

+ 27 - 159
smsb-modules/smsb-system/src/main/java/org/dromara/system/service/impl/SysOssServiceImpl.java

@@ -37,10 +37,9 @@ import org.springframework.http.MediaType;
 import org.springframework.stereotype.Service;
 import org.springframework.web.multipart.MultipartFile;
 
-import javax.imageio.ImageIO;
-import java.awt.image.BufferedImage;
-import java.io.*;
-import java.nio.charset.StandardCharsets;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
 import java.util.ArrayList;
@@ -213,22 +212,12 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
         return buildResultEntity(originalfileName, suffix, storage.getConfigKey(), uploadResult);
     }
 
-    /**
-     * 上传 MultipartFile 到对象存储服务,并保存文件信息到数据库
-     *
-     * @param file 要上传的 MultipartFile 对象
-     * @return 上传成功后的 SysOssVo 对象,包含文件信息
-     * @throws ServiceException 如果上传过程中发生异常,则抛出 ServiceException 异常
-     */
+
     @Override
-    public SysOssVo uploadSmsb(MultipartFile file) {
+    public SysOssVo uploadOta(MultipartFile file) {
         // 计算文件的MD5
         String md5 = getFileMD5(file);
-        // 根据Md5查询当前租户下是否存在相同文件
-        SysOssVo sysOss = baseMapper.selectVoOne(new LambdaQueryWrapper<SysOss>().eq(SysOss::getFileMd5, md5));
-        if (sysOss != null) {
-            return null;
-        }
+
         String originalfileName = file.getOriginalFilename();
         String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
         OssClient storage = OssFactory.instance();
@@ -242,22 +231,26 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
         return buildResultEntityV2(originalfileName, suffix, storage.getConfigKey(), uploadResult, md5, file);
     }
 
-    @Override
-    public SysOssVo uploadOta(MultipartFile file) {
-        // 计算文件的MD5
-        String md5 = getFileMD5(file);
+    private String getFileMD5(MultipartFile file) {
+        try (InputStream inputStream = file.getInputStream()) {
+            MessageDigest md = MessageDigest.getInstance("MD5");
+            byte[] buffer = new byte[1024];
+            int bytesRead;
 
-        String originalfileName = file.getOriginalFilename();
-        String suffix = StringUtils.substring(originalfileName, originalfileName.lastIndexOf("."), originalfileName.length());
-        OssClient storage = OssFactory.instance();
-        UploadResult uploadResult;
-        try {
-            uploadResult = storage.uploadSuffix(file.getBytes(), suffix, file.getContentType());
-        } catch (IOException e) {
-            throw new ServiceException(e.getMessage());
+            while ((bytesRead = inputStream.read(buffer)) != -1) {
+                md.update(buffer, 0, bytesRead);
+            }
+
+            byte[] md5Bytes = md.digest();
+            StringBuilder hexString = new StringBuilder();
+            for (byte b : md5Bytes) {
+                hexString.append(String.format("%02x", b));
+            }
+            return hexString.toString();
+        } catch (IOException | NoSuchAlgorithmException e) {
+            e.printStackTrace();
+            return null;
         }
-        // 保存文件信息
-        return buildResultEntityV2(originalfileName, suffix, storage.getConfigKey(), uploadResult, md5, file);
     }
 
     private String getVersionCode(MultipartFile file) {
@@ -287,28 +280,6 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
         return null;
     }
 
-    private String getFileMD5(MultipartFile file) {
-        try (InputStream inputStream = file.getInputStream()) {
-            MessageDigest md = MessageDigest.getInstance("MD5");
-            byte[] buffer = new byte[1024];
-            int bytesRead;
-
-            while ((bytesRead = inputStream.read(buffer)) != -1) {
-                md.update(buffer, 0, bytesRead);
-            }
-
-            byte[] md5Bytes = md.digest();
-            StringBuilder hexString = new StringBuilder();
-            for (byte b : md5Bytes) {
-                hexString.append(String.format("%02x", b));
-            }
-            return hexString.toString();
-        } catch (IOException | NoSuchAlgorithmException e) {
-            e.printStackTrace();
-            return null;
-        }
-    }
-
     @NotNull
     private SysOssVo buildResultEntityV2(String originalfileName, String suffix, String configKey, UploadResult uploadResult, String md5, MultipartFile file) {
         SysOss oss = new SysOss();
@@ -335,82 +306,12 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
         Integer codeRate = 0;
         // apk 文件
         // 获取APK的version code
-        if (oss.getFileSuffix().equals(APK_FILE_SUFFIX)) {
-            String versionCode = getVersionCode(file);
-            oss.setVersionCode(versionCode);
-            oss.setResolution(resolution);
-            oss.setCodeRate(codeRate);
-            oss.setDuration(duration);
-            oss.setFileType(10);
-            return;
-        }
-        // 文件类型
-        Integer fileType = isImage(file) ? 1 : 2;
-        oss.setFileType(fileType);
-        if (fileType == 1) {
-            resolution = getImageResolution(file);
-        }
-        if (fileType == 2) {
-            String videoInfo = getVideoInfo(file);
-            if (StringUtils.isEmpty(videoInfo)) {
-                return;
-            }
-            resolution = videoInfo.split(",")[0].trim();
-            codeRate = Integer.parseInt(videoInfo.split(",")[1].trim()) / 1000;
-            duration = Integer.parseInt(videoInfo.split(",")[2].trim());
-        }
+        String versionCode = getVersionCode(file);
+        oss.setVersionCode(versionCode);
         oss.setResolution(resolution);
         oss.setCodeRate(codeRate);
         oss.setDuration(duration);
-
-    }
-
-    /**
-     * 获取视频的分辨率、码率和时长
-     *
-     * @param file MultipartFile 视频文件
-     * @return 返回视频信息,例如 "1920x1080, 1500kbps, 120.5s",如果失败返回 null。
-     */
-    public static String getVideoInfo(MultipartFile file) {
-        // 将 MultipartFile 转为临时文件
-        File tempFile = convertMultipartFileToFile(file);
-        if (tempFile == null) {
-            return null;
-        }
-        try {
-            // 执行 ffprobe 命令
-            ProcessBuilder pb = new ProcessBuilder(
-                "ffprobe",
-                "-v", "error",
-                "-select_streams", "v:0",
-                "-show_entries", "stream=width,height,bit_rate,duration",
-                "-of", "csv=p=0",
-                tempFile.getAbsolutePath()
-            );
-            Process process = pb.start();
-            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream(), StandardCharsets.UTF_8));
-            String videoInfo = reader.readLine();
-            process.waitFor();
-            tempFile.delete(); // 删除临时文件
-
-            if (videoInfo != null) {
-                // 解析返回数据
-                String[] values = videoInfo.split(",");
-                if (values.length >= 4) {
-                    // 分辨率
-                    String resolution = values[0] + "x" + values[1];
-                    // 视频时长
-                    // String duration = String.format("%.2f", Double.parseDouble(values[3])) + "s";
-                    String duration = values[2].split("\\.")[0];
-                    // 码率 (转换为 kbps)
-                    String bitrate = values[3].split("\\.")[0];
-                    return resolution + ", " + bitrate + ", " + duration;
-                }
-            }
-        } catch (Exception e) {
-            e.printStackTrace();
-        }
-        return null;
+        oss.setFileType(10);
     }
 
     /**
@@ -427,39 +328,6 @@ public class SysOssServiceImpl implements ISysOssService, OssService {
         return null;
     }
 
-    /**
-     * 获取图片的分辨率(宽度和高度)
-     *
-     * @param file 图片文件(MultipartFile)
-     * @return 返回图片的宽度和高度,例如 "1920x1080",如果失败返回 null
-     */
-    public static String getImageResolution(MultipartFile file) {
-        try {
-            BufferedImage image = ImageIO.read(file.getInputStream());
-            if (image != null) {
-                int width = image.getWidth();
-                int height = image.getHeight();
-                // 返回格式:1920x1080
-                return width + "x" + height;
-            }
-        } catch (IOException e) {
-            e.printStackTrace();
-        }
-        // 读取失败
-        return null;
-    }
-
-    /**
-     * 判断文件是否是图片
-     *
-     * @param file 上传的文件
-     * @return true:是图片,false:不是
-     */
-    public static boolean isImage(MultipartFile file) {
-        String contentType = file.getContentType();
-        return contentType != null && contentType.startsWith("image/");
-    }
-
     /**
      * 判断文件是否是视频
      *

+ 1 - 1
smsb-plus-ui/src/components/SmsbFileUpload/index.vue

@@ -68,7 +68,7 @@ const number = ref(0);
 const uploadList = ref<any[]>([]);
 
 const baseUrl = import.meta.env.VITE_APP_BASE_API;
-const uploadFileUrl = ref(baseUrl + '/resource/oss/upload/smsb'); // 上传文件服务器地址
+const uploadFileUrl = ref(baseUrl + '/source/minioData/upload/smsb'); // 上传文件服务器地址
 const headers = ref(globalHeaders());
 
 const fileList = ref<any[]>([]);

+ 1 - 1
smsb-plus-ui/vite.config.ts

@@ -25,7 +25,7 @@ export default defineConfig(({ mode, command }: ConfigEnv): UserConfig => {
       open: true,
       proxy: {
         [env.VITE_APP_BASE_API]: {
-          target: 'http://localhost:8080',
+          target: 'http://localhost:8084/prod-api',
           changeOrigin: true,
           ws: true,
           rewrite: (path) => path.replace(new RegExp('^' + env.VITE_APP_BASE_API), '')