java使用策略实现多种上传方式

 前言

  最近看到了一种上传的方式,通过配置文件可以更换自己的上传方式,例如本地上传或者是阿里云oss上传方式,于是大概总结了一番。

 策略模式

策略模式(Strategy Pattern):定义一族算法类,将每个算法分别封装起来,让它们可以互相替换。将每个算法封装在一个具有共同接口的独立类中,使得它们可以相互替换。策略模式让算法的变化独立于使用它们的客户端。

策略模式的主要优点是:

  1. 策略模式提供了一种将算法家族封装起来的方式,这有助于简化代码和提高代码的可读性、可维护性和可扩展性。
  2. 策略模式支持开闭原则(Open-Closed Principle),在不修改现有代码的情况下添加新的算法。
  3. 策略模式可以让客户端根据需要选择不同的算法实现,从而实现不同的行为。

 正文

一般使用策略模式步骤是:抽象策略-具体策略A-具体策略B-上下文-客户端。

我们可以定义一个抽象策略Strategy,以及多个具体策略例如ConcreteStrategyAConcreteStrategyBContext类负责持有一个策略对象的引用,并提供设置和执行策略的方法。客户端可以根据需要选择不同的策略实现,并通过上下文对象来执行相应的策略。

实现步骤

yml配置

# 文件上传策略 local、oss
upload:
  strategy: local
  local:
    # nginx映射本地文件路径
    url: https://192.168.0.1:8080
    # 本地文件存储路径
    path: D:/java/file
  # oss存储
  oss:
    url: http://Bucket域名/
    endpoint: your-oss-endpoint
    bucketName: your-oss-bucket-name
    accessKeyId: your-oss-access-key-id
    accessKeySecret: your-oss-access-key-secret
阿里的配置
@Data
@Configuration
@ConfigurationProperties(prefix = "upload.oss")
public class OssProperties {

    /**
     * oss域名
     */
    private String url;

    /**
     * 终点
     */
    private String endpoint;

    /**
     * 访问密钥id
     */
    private String accessKeyId;

    /**
     * 访问密钥密码
     */
    private String accessKeySecret;

    /**
     * bucket名称
     */
    private String bucketName;
}

一 抽象策略

public interface UploadStrategy {
    /**
     * 上传文件
     *
     * @param file 文件
     * @param path 上传路径
     * @return {@link String} 文件地址
     */
    String uploadFile(MultipartFile file, String path);
​
@Service
public abstract class AbstractUploadStrategyImpl implements UploadStrategy {

    @Override
    public String uploadFile(MultipartFile file, String path) {
        try {
            // 获取文件md5值
            String md5 = FileUtils.getMd5(file.getInputStream());
            // 获取文件扩展名
            String extName = FileUtils.getExtension(file);
            // 重新生成文件名
            String fileName = md5 + "." + extName;
            // 判断文件是否已存在
            if (!exists(path + fileName)) {
                // 不存在则继续上传
                upload(path, fileName, file.getInputStream());
            }
            // 返回文件访问路径
            return getFileAccessUrl(path + fileName);
        } catch (Exception e) {
            e.printStackTrace();
            throw new ServiceException("文件上传失败");
        }
    }


    /**
     * 上传
     *
     * @param path        路径
     * @param fileName    文件名
     * @param inputStream 输入流
     * @throws IOException io异常
     */
    public abstract void upload(String path, String fileName, InputStream inputStream) throws IOException;

​
工具类

这里我们用到了一些工具类

@Log4j2
public class FileUtils {

    /**
     * 获取文件md5值
     *
     * @param inputStream 文件输入流
     * @return {@link String} 文件md5值
     */
    public static String getMd5(InputStream inputStream) {
        String md5 = null;
        try {
            md5 = DigestUtils.md5DigestAsHex(inputStream);
        } catch (Exception e) {
            log.error("get md5 error, {}", e.getMessage());
        }
        return md5;
    }

    /**
     * 获取文件名的后缀
     *
     * @param file 表单文件
     * @return 后缀名
     */
    public static String getExtension(MultipartFile file) {
        String extension = FilenameUtils.getExtension(file.getOriginalFilename());
        if (StringUtils.isEmpty(extension)) {
            extension = MimeTypeUtils.getExtension(Objects.requireNonNull(file.getContentType()));
        }
        return extension;
    }

    /**
     * 转换file
     *
     * @param multipartFile 文件
     * @return {@link File} 临时文件
     */
    public static File multipartFileToFile(MultipartFile multipartFile) {
        File tempFile = null;
        try {
            // 获取文件md5值
            String md5 = getMd5(multipartFile.getInputStream());
            // 获取文件扩展名
            String extName = getExtension(multipartFile);
            // 重新生成文件名
            String fileName = md5 + extName;
            // 创建临时文件
            tempFile = File.createTempFile(fileName, extName);
            multipartFile.transferTo(tempFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return tempFile;
    }

二 本地上传

    /**
     * 访问url
     */
    @Value("${upload.local.url}")
    private String localUrl;


    @Override
    public void upload(String path, String fileName, InputStream inputStream) throws IOException {
        // 判断目录是否存在
        File directory = new File(localPath + path);
        if (!directory.exists()) {
            if (!directory.mkdirs()) {
                throw new ServiceException("创建目录失败");
            }
        }
        // 写入文件
        File file = new File(localPath + path + fileName);
        if (file.createNewFile()) {
            try (BufferedInputStream bis = new BufferedInputStream(inputStream);
                 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file))) {
                byte[] bytes = new byte[4096];
                int length;
                while ((length = bis.read(bytes)) != -1) {
                    bos.write(bytes, 0, length);
                }
            }
        }
    }

public class MimeTypeUtils {
    public static final String IMAGE_PNG = "image/png";

    public static final String IMAGE_JPG = "image/jpg";

    public static final String IMAGE_JPEG = "image/jpeg";

    public static final String IMAGE_BMP = "image/bmp";

    public static final String IMAGE_GIF = "image/gif";

    public static String getExtension(String prefix) {
        switch (prefix) {
            case IMAGE_PNG:
                return "png";
            case IMAGE_JPG:
                return "jpg";
            case IMAGE_JPEG:
                return "jpeg";
            case IMAGE_BMP:
                return "bmp";
            case IMAGE_GIF:
                return "gif";
            default:
                return "";
        }
    }
}

三 阿里oss上传

@Slf4j
@Service("ossUploadStrategyImpl")
public class OssUploadStrategyImpl extends AbstractUploadStrategyImpl {

    @Autowired
    private OssProperties ossProperties;

    @Override
    public Boolean exists(String filePath) {
        return getOssClient().doesObjectExist(ossProperties.getBucketName(), filePath);
    }

    @Override
    public void upload(String path, String fileName, InputStream inputStream) {
        OSS ossClient = getOssClient();
        try {
            // 调用oss方法上传
            ossClient.putObject(ossProperties.getBucketName(), path + fileName, inputStream);
        } catch (OSSException oe) {
            log.error("Error Message:" + oe.getErrorMessage());
            log.error("Error Code:" + oe.getErrorCode());
            log.info("Request ID:" + oe.getRequestId());
            log.info("Host ID:" + oe.getHostId());
        } catch (ClientException ce) {
            log.error("Caught an ClientException, Error Message:" + ce.getMessage());
        } finally {
            if (ossClient != null) {
                ossClient.shutdown();
            }
        }
    }

四 定义上下文

@Service
public class UploadStrategyContext {
    /**
     * 上传模式
     */
    @Value("${upload.strategy}")
    private String uploadStrategy;

    @Autowired
    private Map<String, UploadStrategy> uploadStrategyMap;

    /**
     * 上传文件
     *
     * @param file 文件
     * @param path 路径
     * @return {@link String} 文件地址
     */
    public String executeUploadStrategy(MultipartFile file, String path) {
        return uploadStrategyMap.get(getStrategy(uploadStrategy)).uploadFile(file, path);
    }

这里我们还需要一些枚举类,来选择配置,通过配置文件所写的上传模式来选择具体的实现类。

@Getter
@AllArgsConstructor
public enum UploadModeEnum {

    /**
     * 本地
     */
    LOCAL("local", "localUploadStrategyImpl"),

    /**
     * oss
     */
    OSS("oss", "ossUploadStrategyImpl"),

    /**
     * 模式
     */
    private final String mode;

    /**
     * 策略
     */
    private final String strategy;

    /**
     * 获取策略
     *
     * @param mode 模式
     * @return 搜索策略
     */
    public static String getStrategy(String mode) {
        for (UploadModeEnum value : UploadModeEnum.values()) {
            if (value.getMode().equals(mode)) {
                return value.getStrategy();
            }
        }
        return null;
    }
}

最后我们来写一个客户端上传文件的例子

controller
@RestController
@RequestMapping("/admin")
@Slf4j
public class FileController {
    @Autowired
    private FubenFileService fubenFileService;
    @ApiOperation(value = "上传文件")
    @ApiImplicitParam(name = "file", value = "图片", required = true, dataType = "MultipartFile")
    @PostMapping("/file/upload")
    public Result<?> uploadFile(@RequestParam("file") MultipartFile file, @RequestParam("path") String path) {
        fubenFileService.uploadFile(file, path);
        return Result.success("上传成功!");
    }
ServiceImpl

这里我就直接写实现了

@Autowired
    private FubenFileMapper fubenFileMapper;

    @Autowired
    private UploadStrategyContext uploadStrategyContext;
    @Override
    public void uploadFile(MultipartFile file, String path) {
        try {
            String uploadPath = "/".equals(path) ? path : path + "/";
            // 上传文件
            String url = uploadStrategyContext.executeUploadStrategy(file, uploadPath);
            // 获取文件md5值
            String md5 = FileUtils.getMd5(file.getInputStream());
            // 获取文件扩展名
            String extName = FileUtils.getExtension(file);
            FubenFile existFile = fubenFileMapper.selectOne(new LambdaQueryWrapper<FubenFile>()
                    .select(FubenFile::getId)
                    .eq(FubenFile::getFileName, md5)
                    .eq(FubenFile::getFilePath, path));
            Assert.isNull(existFile, "文件已存在");
            // 保存文件信息
            FubenFile newFile = FubenFile.builder()
                    .fileUrl(url)
                    .fileName(md5)
                    .filePath(path)
                    .fileType(extName)
                    .fileSize((int) file.getSize())
                    .isDir(FALSE)
                    .createTime(DateTime.now())
                    .build();
            fubenFileMapper.insert(newFile);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值