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();
        }
    }

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
达内java培训目录 课程 课程内容 课程目标 第一阶段(以Tetris项目贯穿) Java语言基础 算法基础、常用数据结构、企业编程规范。 掌握常见的数据结构和实用算法;培养良好的企业级编程习惯。 Java面向对象 面向对象特性:封装、继承、多态等,面向对象程序设计,基础设计模式等。 掌握面向对象的基本原则以及在编程实践中的意义;掌握Java面向对象编程基本实现原理。 实训项目一:Tetris项目开发 第二阶段(以T-DMS V1项目贯穿) JDK核心API 语言核心包、异常处理、常用工具包、集合框架。 熟练掌握JDK核心API编程技术;理解API设计原则;具备熟练的阅读API文档的能力;为后续的课程学习打下坚实的语言基础。 JavaSE核心 异常处理、多线程基础、IO系统、网络编程、Java反射机制、JVM性能调优(JVM内存结构剖析、GC分析及调优、JVM内存参数优化)、Java泛型、JDK新特性 熟练掌握JavaSE核心内容,特别是IO和多线程;初步具备面向对象设计和编程的能力;掌握基本的JVM优化策略。 实训项目二:T-DMS V1项目开发 第三阶段(以T-DMS V2项目贯穿) SQL语言 SQL语句基础和提高、SQL语句调优。 熟练的掌握SQL语句;掌握一定的数据库查询技巧及SQL语句优化技巧。 Oracle数据库 Oracle体系结构及系统管理、Oracle数据库高级编程、数据库设计基础。 掌握Oracle体系结构及核心编程技术 XML XML语法、XML解析(SAX、DOM)、Dom4j组件、Digester组件。 熟练掌握XML语法规则;理解DOM模型;熟悉Java对XML的各种解析方式。 实训项目三:T-DMS V2项目开发 第四阶段(以T-NetCTOSS电信计费系统贯穿) HTML/CSS TML基本文档结构、掌握CSS基础语法、关于HTML文档块、链接、列表、表格、表单等 掌握HTML基本原理;掌握CSS各种选择器及常见样式设置;熟练使用HTML常用元素。 JavaScript核心 JavaScript语言基础(数据类型、函数、对象、闭包)、Java DOM编程、事件模型、JavaScript面向对象编程。 深入理解JavaScript语言原理;熟练的使用JavaScript对HTML DOM进行编程;熟练掌握JavaScript对象对象封装技巧,为后续的JavaScript学习打下坚实的基础。 Servlet/JSP Servlet生命周期及Servlet服务器、Tomcat部署配置、JSP语法、自定义标记、JSTL和EL表达式、JSP 新特性、Java Web 设计模式。 透彻理解Servlet核心原理;熟练掌握Servlet API;透彻理解JSP引擎工作原理;透彻理解标记库原理;熟悉常见的Java Web设计模式;为后续的Java Web开发打下坚实的理论基础。 Ajax Ajax基础、XHR对象、Ajax设计模式、JSON技术。 掌握Ajax的基本通信原理;掌握基于XML和JSON的Ajax数据规则。 Struts2 Struts2核心控制流程、Ognl、Action、Interceptor、Result、FreeMarker、Struts2标记库、Struts2扩展、Struts2应用技巧(输入验证、消息国际化、文件上传和下载、防止重复提交等)。 熟练掌握Struts2核心要件,特别是Interceptor和Result;掌握基于模板技术的Struts2 UI组件;掌握基于Ognl的数据共享方式、掌握Struts2各种定制及扩展方式;熟练掌握基于Struts2的Web开发技巧。 实训项目四:T-NetCTOSS电信计费系统项目开发 第五阶段(以"航空订票"真SS实电项信目计为费贯系穿统项贯目穿) 企业应用开发部署环境 Linux高级命令集脚本编程、远程登录、Ant、单元测试技术、Maven构建技术、SVN应用技术。 熟练掌握基于Linux系统的操作技能;可以熟练的完成应用的部署工作;可以熟练的使用开发部署工具。 Struts2 Struts2核心控制流程、Ognl、Action、Interceptor、Result、FreeMarker、Struts2标记库、Struts2扩展、Struts2应用技巧(输入验证、消息国际化、文件上传和下载、防止重复提交等)。 熟练掌握Struts2核心要件,特别是Interceptor和Result;掌握基于模板技术的Struts2 UI组件;掌握基于Ognl的数据共享方式、掌握Struts2各种定制及扩展方式;熟练掌握基于Struts2的Web开发技巧。 持久层框架技术 ORM概念、Hibernate核心API、Hibernate实体映射技术、Hibernate关系映射技巧、HQL查询、OSCache及Hibernate缓存技术; 掌握JQuery核心API;了解JQuery基本设计原则;了解多种JQuery插件;掌握DWR的基本原理及应用技巧。 Spring技术 Spring Ioc基础、Ioc注入技巧、对象高级装配(自动装配、模板装配、组件扫描特性、FactoryBean、对象生命周期)、Spring AOP原理、AspectJ、Spring JDBC支持、Spring事务及安全管理;Spring整合Hibernate、Spring整合Struts,SpringMVC技术。 深入理解Ioc和AOP的基本原理和实现方式;熟练掌握SpringIoc及AOP实现方式;熟练掌握Spring事务管理;熟练掌握Spring与其他组件的整合技术。• 实训项目五:企业级应用"航空订票"系统开发 1.开班前 5天训练营将完成"捕鱼达人"游戏项目 2.开班 2周内,贯穿着 Java 语言基础及 Java 面向对象的学习,学员将完成专业版的俄罗斯方块项目 3. 开班一个半月内,贯穿 JavaSE及数据库的学习将完成 T-DMS 数据 采集系统项目 • 整个系统由 DMS 采集端、DMS 服务器端和 NetCTOSS 服务器端三部分组成。 • DMS 采集端的主要任务是采集、解析、匹配和保存并发送登录数据。 • DMS采集端程序采集、解析获得数据后,将处理好的数据发送到DMS 服务器端。 • DMS 服务器端接收到日志数据,将日志数据备份保存为文件数据,并存入数据库。 4.开班3个月内,贯穿Web前端技术和Servlet/JSP的学习,将完成T-NetCTOSS项目: NetCTOSS系统的主要任务是提供对于数据的计费查询和管理功能。 5.开班4个月内,学员将完成"航空订票"真实项目

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值