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

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);
    }
}
相关推荐
平生不喜凡桃李2 分钟前
浅谈 Linux 中 namespace 相关系统调用
java·linux·服务器
zb200641208 分钟前
CVE-2024-38819:Spring 框架路径遍历 PoC 漏洞复现
java·后端·spring
2401_8955213418 分钟前
spring-ai 下载不了依赖spring-ai-openai-spring-boot-starter
java·人工智能·spring
桦026 分钟前
【C++复习】:继承
开发语言·c++
何仙鸟1 小时前
GarmageSet下载和处理
java·开发语言
wefly20171 小时前
免安装!m3u8live.cn在线 M3U8 播放器,小白也能快速上手
java·开发语言·python·json·php·m3u8·m3u8在线转换
yuweiade1 小时前
springboot和springframework版本依赖关系
java·spring boot·后端
ywf12151 小时前
springboot设置多环境配置文件
java·spring boot·后端
小马爱打代码1 小时前
SpringBoot + 消息生产链路追踪 + 耗时分析:从创建到发送,全链路性能可视化
java·spring boot·后端
薛先生_0991 小时前
js学习语法第一天
开发语言·javascript·学习