车车 4 mēneši atpakaļ
vecāks
revīzija
afaa522f06

+ 6 - 0
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java

@@ -109,4 +109,10 @@ public class FileController {
         return success(BeanUtils.toBean(pageResult, FileRespVO.class));
     }
 
+    @PostMapping("/uploadLocalFile")
+    @Operation(summary = "生物识别文件上传到本地", description = "模式三:生物识别文件上传到本地")
+    public CommonResult<FileDO> uploadLocalFile(FileUploadReqVO uploadReqVO) throws Exception {
+        return success(fileService.uploadLocalFile(uploadReqVO));
+    }
+
 }

+ 2 - 0
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigService.java

@@ -82,4 +82,6 @@ public interface FileConfigService {
      */
     FileClient getMasterFileClient();
 
+    FileClient getFileClientByName(String name);
+
 }

+ 11 - 0
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileConfigServiceImpl.java

@@ -1,6 +1,7 @@
 package cn.iocoder.yudao.module.infra.service.file;
 
 import cn.hutool.core.io.resource.ResourceUtil;
+import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.IdUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
@@ -14,6 +15,7 @@ import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;
 import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientConfig;
 import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClientFactory;
 import cn.iocoder.yudao.module.infra.framework.file.core.enums.FileStorageEnum;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
 import jakarta.annotation.Resource;
@@ -186,4 +188,13 @@ public class FileConfigServiceImpl implements FileConfigService {
         return clientCache.getUnchecked(CACHE_MASTER_ID);
     }
 
+    @Override
+    public FileClient getFileClientByName(String name) {
+        Assert.notBlank(name, "查询的配置不能为空!");
+        FileConfigDO fileConfigDO = fileConfigMapper.selectOne(Wrappers.<FileConfigDO>lambdaQuery()
+                .eq(FileConfigDO::getName, name));
+        Assert.isTrue(fileConfigDO != null, "文件上传配置不存在,请配置【生物识别存储器】!");
+        return clientCache.getUnchecked(fileConfigDO.getId());
+    }
+
 }

+ 8 - 0
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java

@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileCreateReqVO;
 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO;
 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;
+import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileUploadReqVO;
 import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
 import jakarta.validation.constraints.NotEmpty;
 
@@ -68,4 +69,11 @@ public interface FileService {
      */
     byte[] getFileContent(Long configId, String path) throws Exception;
 
+    /**
+     *
+     * @param uploadReqVO
+     * @return
+     */
+    FileDO uploadLocalFile(FileUploadReqVO uploadReqVO) throws Exception;
+
 }

+ 50 - 2
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.infra.service.file;
 
 import cn.hutool.core.date.LocalDateTimeUtil;
 import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.lang.Assert;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.crypto.digest.DigestUtil;
@@ -10,6 +11,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileCreateReqVO;
 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePageReqVO;
 import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FilePresignedUrlRespVO;
+import cn.iocoder.yudao.module.infra.controller.admin.file.vo.file.FileUploadReqVO;
 import cn.iocoder.yudao.module.infra.dal.dataobject.file.FileDO;
 import cn.iocoder.yudao.module.infra.dal.mysql.file.FileMapper;
 import cn.iocoder.yudao.module.infra.framework.file.core.client.FileClient;
@@ -19,6 +21,7 @@ import com.google.common.annotations.VisibleForTesting;
 import jakarta.annotation.Resource;
 import lombok.SneakyThrows;
 import org.springframework.stereotype.Service;
+import org.springframework.web.multipart.MultipartFile;
 
 import static cn.hutool.core.date.DatePattern.PURE_DATE_PATTERN;
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
@@ -34,13 +37,13 @@ public class FileServiceImpl implements FileService {
 
     /**
      * 上传文件的前缀,是否包含日期(yyyyMMdd)
-     *
+     * <p>
      * 目的:按照日期,进行分目录
      */
     static boolean PATH_PREFIX_DATE_ENABLE = true;
     /**
      * 上传文件的后缀,是否包含时间戳
-     *
+     * <p>
      * 目的:保证文件的唯一性,避免覆盖
      * 定制:可按需调整成 UUID、或者其他方式
      */
@@ -171,4 +174,49 @@ public class FileServiceImpl implements FileService {
         return client.getContent(path);
     }
 
+    @Override
+    public FileDO uploadLocalFile(FileUploadReqVO uploadReqVO) throws Exception {
+        // uploadReqVO.setDirectory("finger/1");  // 生成后的路径/home/finger/120250701/file.jpg
+        MultipartFile file = uploadReqVO.getFile();
+        byte[] content = IoUtil.readBytes(file.getInputStream());
+        Assert.isTrue(content.length != 0, "文件内容不能为空!");
+        String name = file.getOriginalFilename();
+        String directory = uploadReqVO.getDirectory();
+        String type = file.getContentType();
+        // 1.1 处理 type 为空的情况
+        if (StrUtil.isEmpty(type)) {
+            type = FileTypeUtils.getMineType(content, name);
+        }
+        // 1.2 处理 name 为空的情况
+        if (StrUtil.isEmpty(name)) {
+            name = DigestUtil.sha256Hex(content);
+        }
+        if (StrUtil.isEmpty(FileUtil.extName(name))) {
+            // 如果 name 没有后缀 type,则补充后缀
+            String extension = FileTypeUtils.getExtension(type);
+            if (StrUtil.isNotEmpty(extension)) {
+                name = name + extension;
+            }
+        }
+
+        // 2.1 生成上传的 path,需要保证唯一
+        String path = generateUploadPath(name, directory);
+        // 2.2 上传到文件存储器
+        FileClient client = fileConfigService.getFileClientByName("生物识别存储器");
+        Assert.notNull(client, "客户端(master) 不能为空");
+        String url = client.upload(content, path, type);
+
+        // 3. 保存到数据库
+        fileMapper.insert(new FileDO().setConfigId(client.getId())
+                .setName(name).setPath(path).setUrl(url)
+                .setType(type).setSize(content.length));
+        FileDO fileDO = new FileDO();
+        fileDO.setName(name);
+        fileDO.setPath(path);
+        fileDO.setUrl(url);
+        fileDO.setType(type);
+        fileDO.setSize(content.length);
+        return fileDO;
+    }
+
 }

+ 8 - 1
yudao-server/src/main/resources/application.yaml

@@ -315,4 +315,11 @@ yudao:
 debug: false
 # 插件配置 TODO 芋艿:【IOT】需要处理下
 pf4j:
-  pluginsDir: /Users/anhaohao/code/gitee/ruoyi-vue-pro/plugins # 插件目录
+  pluginsDir: /Users/anhaohao/code/gitee/ruoyi-vue-pro/plugins # 插件目录
+
+# 本地文件配置
+local-file:
+  # 指纹
+  finger-path: finger
+  # 人脸
+  face-path: face