反射-实现租户聚合文件上传

140 阅读2分钟

唠嗑前言

个人工作开发,备注学习和开源记录!!!

代码比较简单、有点开发技术的应该都看得懂(虽然本人工作五年经验了,但还是小小开发 哈哈哈)

主要是理解设计理念:利用反射机制, 实现租户数据库存各云存储配置、走不同上传方式。

可广泛扩展之类啥的、不仅文件上传,还有发短信、发邮件,希望对长江前后浪开发学习有所帮助。

封装common-dfs包

  • 1、定义一个文件上传接口
public interface IDfsBaseService {
    /**
     * 文件上传接口
     */
    FileInfo uploadFile(MultipartFile file) throws Exception;
}

封装commoon-dfs-minio包

  • 1、实现上传接口
@Slf4j
@RequiredArgsConstructor
public class MinioFileServiceImpl implements IDfsBaseService {

    private final MinioClient minioClient;
    private final MinioOssProperties minioOssProperties;

    @Override
    public FileInfo uploadFile(MultipartFile file) throws Exception {
        String fileName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"))
                + StrUtil.SLASH+ IdUtil.simpleUUID() + StrUtil.DOT + FileUploadUtils.getExtension(file);

        minioClient.putObject(PutObjectArgs.builder()
                .bucket(minioOssProperties.getBucket())
                .stream(file.getInputStream(), file.getSize(), -1)
                .contentType(file.getContentType())
                .object(fileName)
                .build());
        //记录下预览地址
        String url = minioClient.getPresignedObjectUrl(
                        GetPresignedObjectUrlArgs.builder()
                                .method(Method.GET)
                                .bucket(minioOssProperties.getBucket())
                                .object(fileName)
//                                .expiry(1, TimeUnit.DAYS)
                                .build());
        FileInfo fileInfo = new FileInfo();
        fileInfo.setFileName(FileUtils.getName(fileName));
        fileInfo.setUrl(url);
        fileInfo.setType(FileUtil.extName(fileName));
        return fileInfo;
    }
}

以此类推其它上传方案 如阿里、腾讯、七牛等存储方式

封装commoon-dfs-qiniu包

@Slf4j
@RequiredArgsConstructor
public class QiniuFileImpl implements IDfsBaseService {
    @Override
    public FileInfo uploadFile(MultipartFile file) throws Exception {
        return null;
    }
}

封装commoon-dfs-aliyun包

@Slf4j
@RequiredArgsConstructor
public class AliyunFileServiceImpl implements IDfsBaseService {
    @Override
    public FileInfo uploadFile(MultipartFile file) throws Exception {
        return null;
    }
}

封装commoon-dfs-tencent包

@Slf4j
@RequiredArgsConstructor
public class TencentFileImpl implements IDfsBaseService {
    @Override
    public FileInfo uploadFile(MultipartFile file) throws Exception {
        return null;
    }
}

重点开始-反射

springboot-web实战

  • 1、定义个获得租户文件上传工厂类
@Component
public class DfsFactory {
    /**
     * DfsService 缓存
     */
    private final static Map<Long, IDfsBaseService> dfsBaseServiceMap = new ConcurrentHashMap<>();

    /**
     * 通过反射静态方法获取 DfsService
     *
     * @param dfsConfig 分布式存储配置
     * @return dfsService
     */
    public IDfsBaseService getDfsBaseService(DfsConfig dfsConfig) {
        //根据dfsId获取对应的分布式存储服务接口,dfsId是唯一的,每个租户有其自有的dfsId
        Long dfsId = dfsConfig.getId();
        IDfsBaseService dfsBaseService = dfsBaseServiceMap.get(dfsId);
        if (null == dfsBaseService) {
            Class cls = null;
            try {
                //获取目标
                cls = Class.forName(DfsFactoryClassEnum.getValue(dfsConfig.getDfsType()));

                Method staticMethod = cls.getDeclaredMethod("getDfsBaseService", DfsConfig.class);
                dfsBaseService = (IDfsBaseService) staticMethod.invoke(cls, dfsConfig);
                dfsBaseServiceMap.put(dfsId, dfsBaseService);
            } catch (Exception e) {
                throw new ServiceException("分布式存储配置错误,上传失败");
            }
        }
        return dfsBaseService;
    }
}
  • 2、枚举
/**
 * 分布式存储工厂类枚举
 */
public enum DfsFactoryClassEnum {

    /**
     * MINIO
     */
    MINIO("minio", "com.lzmh.tools.service.factory.DfsMinioFactory", "MINIO存储"),

    /**
     * 七牛云
     */
    QI_NIU("qiniu", "com.lzmh.tools.service.factory.DfsQiniuFactory", "七牛云存储"),

    /**
     * 阿里云
     */
    ALI_YUN("aliyun", "com.lzmh.tools.service.factory.DfsAliyunFactory", "阿里云存储"),

    /**
     * 腾讯云
     */
    TENCENT("tencent", "com.lzmh.tools.service.factory.DfsTencentFactory", "腾讯云存储");

    @Getter
    private String code;

    @Getter
    private String value;

    @Getter
    private String title;

    DfsFactoryClassEnum(String code, String value, String title) {
        this.code = code;
        this.value = value;
        this.title = title;
    }

    public static String getValue(String code) {
        DfsFactoryClassEnum[] smsFactoryClassEnums = values();
        for (DfsFactoryClassEnum smsFactoryClassEnum : smsFactoryClassEnums) {
            if (smsFactoryClassEnum.getCode().equals(code)) {
                return smsFactoryClassEnum.getValue();
            }
        }
        throw new BusinessException("暂未实现该分布式存储实现:" + code );
    }

}
  • 3、示例 mino工厂、还有DfsQiniuFactory、DfsAliyunFactory、DfsTencentFactory类似
package com.lzmh.tools.service.factory;
/**
 * Minio存储工厂类
 *
 * @author ycs
 * @date 2023/8/15
 */
public class DfsMinioFactory {

    public static IDfsBaseService getDfsBaseService(DfsConfig dfsConfig) {
        MinioClient minioClient =
                MinioClient.builder()
                        .endpoint(dfsConfig.getUploadUrl())
                        .credentials(dfsConfig.getAccessKey(), dfsConfig.getSecretKey()).build();
        MinioOssProperties minioOssProperties = new MinioOssProperties();
        minioOssProperties.setAccessKey(dfsConfig.getAccessKey());
        minioOssProperties.setSecretKey(dfsConfig.getSecretKey());
        minioOssProperties.setRegion(dfsConfig.getRegion());
        minioOssProperties.setBucket(dfsConfig.getBucket());
        minioOssProperties.setUploadUrl(dfsConfig.getUploadUrl());

        return new MinioFileServiceImpl(minioClient, minioOssProperties);
    }
}

业务开始使用工厂类上传service

/**
 * 文件管理Service业务层处理
 *
 * @author admin
 * @date 2023-08-14
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class DfsFileServiceImpl extends ServicePlusImpl<DfsFileMapper, DfsFile, DfsFile> implements IDfsFileService {

    private final DfsConfigMapper dfsConfigMapper;

    private final DfsFactory dfsFactory;

    @Override
    public DfsFile upload(MultipartFile file, String tenantId) {
        DfsConfig dfsConfig = dfsConfigMapper.selectOne(Wrappers.<DfsConfig>lambdaQuery()
                .eq(DfsConfig::getTenantId, tenantId)
                .eq(DfsConfig::getDfsStatus, Boolean.TRUE));
        if (dfsConfig == null) {
            throw new BusinessException("该租户没有分布式存储配置, 请核查上传配置");
        }
        // 获取租户上传对象
        IDfsBaseService dfsBaseService = dfsFactory.getDfsBaseService(dfsConfig);
        DfsFile dfsFile = new DfsFile();
        dfsFile.setDfsId(dfsConfig.getId())
                .setTenantId(Convert.toLong(tenantId))
                .setOriginal(file.getOriginalFilename())
                .setFileSize(file.getSize());
        try {
            // 执行分布式存储上传
            FileInfo fileInfo = dfsBaseService.uploadFile(file);

            dfsFile.setFileName(fileInfo.getFileName())
                    .setUrl(fileInfo.getUrl())
                    .setType(fileInfo.getType())
                    .setStatus(Boolean.TRUE)
                    .setMd5(DigestUtils.md5DigestAsHex(file.getInputStream()));
            // 文件的MD5值
            // String hash = Etag.data(file.getBytes());
            return dfsFile;
        } catch (Exception e) {
            log.warn("租户上传文件异常,检查配置{}", dfsConfig);
            dfsFile.setStatus(Boolean.FALSE);
            throw new BusinessException(e.getMessage());
        } finally {
            //保存文件记录
            this.save(dfsFile);
        }
    }
}

项目截图

  • 公共包

image.png *实现

image.png

鸣谢开源地址:gitee.com/ychunsheng/…