上传文件相关业务,采用策略模式+模版方法模式进行动态解耦

service 里面是注入的策略上下文

我们用策略上下文去调对应的方法

复制代码
    public String uploadPhoto(MultipartFile file) {
        // 上传文件
        String url = uploadStrategyContext.executeUploadStrategy(file, FilePathEnum.PHOTO.getPath());
        blogFileService.saveBlogFile(file, url, FilePathEnum.PHOTO.getFilePath());
        return url;
    }

策略上下文(StrategyContext)中我们把策略定义成常量,从配置中装载

这个是我们在 yml 里面定义的常量

除了这个常量,策略上下文里面还有一个 map

这个 map 的 key 是 string 策略名称,value 是对应的下载策略

我们通过 我们写在配置文件里面的配置去 map 去调对应的策略

UploadStrategy 是一个接口

这边采用模版方法设计模式

即采用接口+抽象类的方式

我们在接口中定义统一规范

应对当前上传业务,我们只需要一个 uploadFile 方法即可

接着是模版

我们想一下我们的模版应该怎么去写

我们想到每一个实现类,都要写一个上传方法

这对应着我们调不同的客户端去执行相应的方法,uploadFile()这个方法里面会调取这个方法

然后我们就应该能想到,还有一个检查文件是否存在的方法

接着我们还要有一个出口,就是获取文件的路径

毕竟客户端的自己实现逻辑都不同,所以我们都要进行重写

复制代码
package com.ican.strategy.impl;

import com.ican.exception.ServiceException;
import com.ican.strategy.UploadStrategy;
import com.ican.utils.FileUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;

/**
 * 抽象上传模板
 *
 * @author Dduo
 */
@Slf4j
@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;
            // 判断文件是否已存在
            Boolean exists = exists(path + fileName);
            if (!exists) {
                // 不存在则继续上传
                upload(path, fileName, file.getInputStream());
            }
            // 返回文件访问路径
            return getFileAccessUrl(path + fileName);
        } catch (Exception e) {
            log.info("uploadFile fail, error is {}", e.getMessage());
            throw new ServiceException("文件上传失败");
        }
    }

    /**
     * 判断文件是否存在
     *
     * @param filePath 文件路径
     * @return {@link Boolean}
     */
    public abstract Boolean exists(String filePath);

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

    /**
     * 获取文件访问url
     *
     * @param filePath 文件路径
     * @return {@link String} 文件url
     */
    public abstract String getFileAccessUrl(String filePath);
    
}

这边有一个 cos 实现逻辑

仅仅供参考

复制代码
package com.ican.strategy.impl;

import com.ican.config.properties.CosProperties;
import com.qcloud.cos.COSClient;
import com.qcloud.cos.ClientConfig;
import com.qcloud.cos.auth.BasicCOSCredentials;
import com.qcloud.cos.auth.COSCredentials;
import com.qcloud.cos.exception.CosClientException;
import com.qcloud.cos.exception.CosServiceException;
import com.qcloud.cos.http.HttpProtocol;
import com.qcloud.cos.model.ObjectMetadata;
import com.qcloud.cos.region.Region;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.io.InputStream;

/**
 * cos上传策略
 *
 * @author Dduo
 */
@Slf4j
@Service("cosUploadStrategyImpl")
public class CosUploadStrategyImpl extends AbstractUploadStrategyImpl {

    @Autowired
    private CosProperties cosProperties;

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

    @Override
    public void upload(String path, String fileName, InputStream inputStream) {
        COSClient cosClient = getCosClient();
        try {
            ObjectMetadata objectMetadata = new ObjectMetadata();
            // 上传的流如果能够获取准确的流长度,则推荐一定填写 content-length
            objectMetadata.setContentLength(inputStream.available());
            // 调用cos方法上传
            cosClient.putObject(cosProperties.getBucketName(), path + fileName, inputStream, objectMetadata);
        } catch (CosServiceException e) {
            log.error("Error Message:" + e.getErrorMessage());
            log.error("Error Code:" + e.getErrorCode());
            log.info("Request ID:" + e.getRequestId());
        } catch (CosClientException e) {
            log.error("Caught an CosClientException, Error Message:" + e.getMessage());
        } catch (IOException e) {
            log.error("Caught an IOException, Error Message:" + e.getMessage());
        } finally {
            cosClient.shutdown();
        }
    }

    @Override
    public String getFileAccessUrl(String filePath) {
        return cosProperties.getUrl() + filePath;
    }

    /**
     * 获取cosClient
     *
     * @return {@link COSClient} cosClient
     */
    private COSClient getCosClient() {
        // 1 初始化用户身份信息(secretId, secretKey)。
        COSCredentials cred = new BasicCOSCredentials(cosProperties.getSecretId(), cosProperties.getSecretKey());
        // 2 设置 bucket 的地域, COS 地域的简称请参照 https://cloud.tencent.com/document/product/436/6224
        Region region = new Region(cosProperties.getRegion());
        ClientConfig clientConfig = new ClientConfig(region);
        // 这里建议设置使用 https 协议
        // 从 5.6.54 版本开始,默认使用了 https
        clientConfig.setHttpProtocol(HttpProtocol.https);
        // 3 生成 cos 客户端。
        return new COSClient(cred, clientConfig);
    }
}
相关推荐
自由随风飘17 小时前
python 题目练习1~5
开发语言·python
cynicme18 小时前
力扣3318——计算子数组的 x-sum I(偷懒版)
java·算法·leetcode
Bony-18 小时前
Go语言完全学习指南 - 从基础到精通------语言基础篇
服务器·开发语言·golang
青云交19 小时前
Java 大视界 -- Java 大数据在智能教育学习效果评估与教学质量改进实战
java·实时分析·生成式 ai·个性化教学·智能教育·学习效果评估·教学质量改进
崎岖Qiu19 小时前
【设计模式笔记17】:单例模式1-模式分析
java·笔记·单例模式·设计模式
fl17683119 小时前
基于python的天气预报系统设计和可视化数据分析源码+报告
开发语言·python·数据分析
Lei活在当下19 小时前
【现代 Android APP 架构】09. 聊一聊依赖注入在 Android 开发中的应用
java·架构·android jetpack
ACP广源盛1392462567320 小时前
(ACP广源盛)GSV6172---MIPI/LVDS 信号转换为 Type-C/DisplayPort 1.4/HDMI 2.0 并集成嵌入式 MCU
c语言·开发语言·单片机·嵌入式硬件·音视频
不穿格子的程序员20 小时前
从零开始刷算法-栈-括号匹配
java·开发语言·
雪域迷影20 小时前
C#中通过get请求获取api.open-meteo.com网站的天气数据
开发语言·http·c#·get