本文记录在campusai智慧园区项目中,如何基于若依(RuoYi)框架实现MinIO分布式文件存储的完整技术方案。重点剖析配置化存储策略、MinIO集成细节与工程化设计,解决本地磁盘存储的单点故障问题。不仅关注技术实现,更深入探讨架构设计背后的思考与最佳实践。

一、问题与场景:本地磁盘存储的局限性及其工程化挑战

在智慧园区项目初期,我们采用了若依框架默认的本地磁盘存储方案。这种方案看似简单直接,但在实际运行中逐渐暴露出诸多问题,尤其是在企业级应用场景下,其局限性愈发明显。

文件与服务器强绑定的致命弱点

本地磁盘存储的核心问题在于文件与服务器形成了强绑定关系。当管理员在若依后台上传一份PDF技术手册时,文件被直接写入服务器的本地磁盘目录。这种设计存在一个致命缺陷:一旦服务器发生硬件故障、系统崩溃或磁盘损坏,所有已上传的文件将瞬间不可用,甚至永久丢失。对于智慧园区系统而言,这意味着学生无法查阅最新的宿舍管理规定,教师无法获取教学资料,整个系统的文档服务功能彻底瘫痪。更糟糕的是,这种故障模式是"全有或全无"的——要么系统正常运行,要么所有文件服务同时中断,没有任何中间状态。

扩展困难,磁盘容量有限带来的发展瓶颈

随着智慧园区业务的不断发展,文档数量呈指数级增长。PDF技术手册、TXT操作指南、社团活动照片、公告通知等文件迅速占满服务器磁盘空间。传统的解决方案是购买更大容量的硬盘或增加服务器,但这不仅成本高昂,而且操作复杂。每次扩容都需要停机迁移数据,对系统可用性造成严重影响。此外,单一服务器的存储容量终究有限,当文档总量达到TB级别时,本地磁盘方案已无法满足业务需求。

数据安全,缺乏冗余备份的高风险隐患

本地磁盘存储缺乏数据冗余机制,文件仅存一份,没有任何备份。这意味着硬盘一旦发生物理损坏,数据将永久丢失。虽然可以通过定时备份脚本缓解此问题,但备份过程本身存在时间窗口,且需要额外的存储设备和复杂的运维操作。在智慧园区这种对数据可靠性要求较高的场景中,这种"裸奔"式的数据存储方式显然不符合企业级应用的标准。

人工干预带来的效率低下

本地存储方案需要大量的手工操作。管理员需要定期清理磁盘空间,监控磁盘使用率,手动执行备份任务。当需要迁移服务器时,更是需要人工拷贝所有文件,操作繁琐且容易出错。这种依赖人工干预的运维模式不仅效率低下,还增加了人为失误的风险。

二、架构升级:MinIO分布式存储方案

面对本地磁盘存储的诸多痛点,我们需要一套能够彻底解决问题的架构方案。MinIO分布式对象存储因其高性能、高可用性和易用性成为我们的首选,但如何将其无缝集成到现有若依框架中,并确保平滑迁移和长期可维护性,成为我们面临的核心挑战。

2.1 设计目标:

我们的设计目标不仅限于解决技术问题,更注重工程化的长期价值:

高可用:彻底解决单点故障

MinIO的分布式架构天然支持多副本存储,数据会自动在多个节点间复制。即使某个节点完全宕机,文件仍可从其他节点正常访问。我们的目标是将文件可用性从本地存储的约99%提升至99.9%以上,满足企业级应用的标准。

最小化系统改造风险

现有系统已稳定运行,任何架构改造都必须确保向后兼容。我们的方案必须支持配置化切换,管理员只需修改一个配置项即可在本地存储和MinIO存储之间无缝切换,无需修改任何业务代码。

降低人工干预成本

桶的创建、配置管理、故障恢复等操作都应实现自动化,减少对运维人员的依赖。应用启动时应自动完成必要的初始化工作,确保系统"开箱即用"。

按需使用避免浪费

MinIO客户端应在需要时才初始化,避免在仅使用本地存储的环境中占用不必要的资源。这种"按需装配"的设计符合微服务架构的最佳实践。

2.2 核心架构:配置化存储策略的工程化实现

我们设计了一套基于配置驱动的存储策略架构,其核心思想是将存储实现与业务逻辑彻底解耦:

若依后台 → 配置化存储策略 → 本地存储/MinIO存储 → 文件访问
    ↑           ↑              ↑              ↑
 CRUD操作     动态选择       本地磁盘/对象存储   统一URL生成

配置驱动的设计思想

通过ruoyi.uploadType配置项控制存储策略,业务代码无需感知底层存储的具体实现。这种设计符合软件工程的"依赖倒置原则"——高层模块不依赖于低层模块,二者都依赖于抽象接口。

统一抽象层的好处

无论底层使用本地存储还是MinIO,上层业务看到的都是统一的接口和行为。这种抽象不仅简化了代码逻辑,还为未来的进一步扩展预留了空间。如果需要支持阿里云OSS、腾讯云COS等其他存储服务,只需添加新的实现,无需修改现有业务代码。

三、关键技术实现:

3.1 配置化存储策略:本地/MinIO动态切换的工程化实现

CommonController中,我们实现了基于配置的动态存储选择机制。这个设计的关键在于将存储策略的选择逻辑从业务代码中完全抽离,实现真正的"配置驱动"。

@PostMapping("/upload")
@ResponseBody
public AjaxResult uploadFile(MultipartFile file) throws Exception {
    try {
        String fileName = null, url = null;
        
        // 配置化存储选择:核心设计理念
        if ("minio".equals(RuoYiConfig.getUploadType())) {
            // MinIO存储路径
            fileName = upload2Minio(file);
            url = MinioConfig.getUrl() + "/" + MinioConfig.getBucket() + "/" + fileName;
        } else {
            // 默认本地存储路径
            String filePath = RuoYiConfig.getUploadPath();
            fileName = FileUploadUtils.upload(filePath, file);
            url = serverConfig.getUrl() + fileName;
        }
        
        AjaxResult ajax = AjaxResult.success();
        ajax.put("url", url);
        ajax.put("fileName", fileName);
        ajax.put("newFileName", FileUtils.getName(fileName));
        ajax.put("originalFilename", file.getOriginalFilename());
        return ajax;
    } catch (Exception e) {
        return AjaxResult.error(e.getMessage());
    }
}

设计价值的深度解析

这个看似简单的条件判断背后蕴含着重要的工程化思想:

  1. 开闭原则

    系统对扩展开放,对修改关闭。新增存储类型只需添加新的条件分支,无需修改现有逻辑。这种设计确保了系统的长期可维护性。

  2. 配置驱动的架构优势

    存储策略的选择完全由外部配置控制,实现了策略与实现的彻底分离。管理员可以根据环境需求(开发/测试/生产)灵活选择存储方案。

  3. 平滑迁移

    通过保留本地存储路径,确保了在MinIO出现问题时可以快速回退。这种"渐进式迁移"策略大大降低了系统改造风险。

3.2 MinIO客户端配置:工程化设计的典范

MinIO客户端的配置类体现了Spring Boot最佳实践与工程化设计的完美结合:

@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {
    private static String host;
    private static String port;
    private static String bucket;
    private static String username;
    private static String password;
    private static String url;

    @Bean
    @Lazy
    @ConditionalOnProperty(name = "ruoyi.uploadType", havingValue = "minio")
    public MinioClient minioClient() throws Exception {
        // 域名转IP解决SDK兼容性问题:工程化细节处理
        if (!InetAddressValidator.getInstance().isValid(host)){
            logger.warn("MinIO:host is not format as ip,change it!");
            InetAddress inetAddress = InetAddress.getByName(host);
            host = inetAddress.getHostAddress();
            logger.info("MinIO:host change to : {}" , host);
        }
        
        MinioClient minioClient = new MinioClient(host, Integer.valueOf(port), username, password, false);

        // 桶自动创建:自动化运维的关键
        boolean found = minioClient.bucketExists(bucket);
        if (!found) {
            minioClient.makeBucket(bucket);
        }
        logger.info("MinIO:bucket init ok , bucket={}" , bucket);
        return minioClient;
    }
    
    // 静态工具方法,便于全局访问
    public static String getBucket() { return bucket; }
    public static String getUrl() { return url; }
    // ... 其他getter/setter
}

关键技术点的深度剖析

  1. 条件化装配

    @ConditionalOnProperty注解确保仅在ruoyi.uploadType=minio时创建MinIO客户端。这种设计避免了在不使用MinIO的环境中占用不必要的资源,体现了"按需使用"的工程化原则。结合@Lazy注解,进一步优化了启动性能,确保资源只在真正需要时才被初始化。

  2. 地址解析:

    MinIO旧版本SDK要求endpoint必须是IP格式,但实际生产环境中我们更倾向于使用域名。我们的地址解析逻辑自动将域名转换为IP,既解决了技术兼容性问题,又保持了配置的语义清晰。这种"技术适配层"的设计体现了工程化思维——不因技术限制而牺牲最佳实践。

  3. 桶自初始化:

    应用启动时自动检查并创建所需存储桶,避免了因桶不存在导致的运行时错误。这种"自初始化"设计大大降低了运维复杂度,确保系统能够"开箱即用",符合DevOps最佳实践。

  4. 静态工具类:

    将配置参数设计为静态字段并提供静态访问方法,实现了全局统一的配置管理。业务代码无需注入配置类实例,直接通过MinioConfig.getBucket()即可获取配置值,既简化了代码,又确保了配置的一致性。

3.3 MinIO上传实现:资源管理的工程化优化

上传功能的实现体现了对资源管理和异常处理的深度思考:

private String upload2Minio(MultipartFile file) {
    // 唯一文件名生成策略:避免覆盖的关键
    String name = Seq.getId(Seq.uploadSeqType) + "." + 
                  FilenameUtils.getExtension(file.getOriginalFilename());
    
    InputStream inputStream = null;
    try {
        inputStream = file.getInputStream();
        minioClient.putObject(
            MinioConfig.getBucket(), 
            name, 
            inputStream, 
            file.getSize(), 
            null, null, 
            file.getContentType()
        );
    } catch (Exception e) {
        throw new RuntimeException(e);
    } finally {
        try {
            // 关键:显式关闭流,避免临时文件残留
            if (inputStream != null) inputStream.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    return name;
}

优化细节的工程化解读

  1. 唯一文件名策略:数据安全的基础保障

    使用Seq.getId()生成唯一文件名,有效避免了文件覆盖问题。在分布式环境中,文件名冲突可能导致严重的数据安全问题,这种设计确保了每个文件的唯一性和可追溯性。

  2. 流式上传:性能与资源管理的平衡

    采用流式上传方式,避免将整个文件加载到内存中。对于大文件(如高清视频、大型PDF文档),这种设计可有效防止内存溢出,确保系统的稳定性。

  3. 资源管理:finally块的正确使用

    在finally块中显式关闭输入流,解决了Spring无法自动清理临时文件的问题。这种资源管理方式体现了"谁申请谁释放"的原则,避免了资源泄漏和文件锁定的问题。

  4. 元数据保留:数据完整性的工程化考量

    完整传递文件大小、内容类型等元数据信息,确保存储在MinIO中的文件信息完整。这种细节处理体现了对数据完整性的重视,是工程化设计的重要体现。

四、配置文件与工作流程:

4.1 配置文件:配置驱动的核心载体

# application.yml
minio:
  host: minio.example.com
  port: 9000
  bucket: campusai-docs
  username: admin
  password: password123
  url: http://minio.example.com:9000

ruoyi:
  uploadType: minio  # 关键配置:控制存储策略

配置设计的工程化思考

  • 环境隔离:不同环境(开发/测试/生产)可使用不同的MinIO配置

  • 安全考虑:敏感信息(密码)通过配置管理,避免硬编码

  • 灵活切换uploadType配置项实现存储策略的动态切换

4.2 完整工作流程:从启动到上传的详细解析

启动阶段:自动化初始化的工程化实践

  1. Spring启动解析minio前缀配置,注入MinioConfig静态字段

  2. 检测ruoyi.uploadType=minio,触发MinIO客户端创建条件

  3. 域名解析:minio.example.com192.168.1.100(解决SDK限制)

  4. 桶检查:自动创建campusai-docs桶(自动化运维)

  5. 连接测试:验证MinIO服务可用性

上传阶段:业务逻辑的完整执行

  1. 前端调用/common/upload接口上传PDF文档

  2. 后端根据配置选择MinIO存储策略

  3. 生成唯一文件名:20250218_abc123.pdf

  4. 流式上传至MinIO集群

  5. 返回访问URL:http://minio.example.com:9000/campusai-docs/20250218_abc123.pdf

访问阶段:高可用性的体现

  • 通过生成的URL直接访问文件

  • MinIO支持权限控制(私有/公开桶)

  • 支持CDN集成加速文件访问

  • 多副本机制确保文件高可用

五、设计亮点与价值

5.1 配置化存储策略

开闭原则的完美体现

系统对扩展开放,对修改关闭。新增存储类型只需添加新的实现,无需修改现有业务代码。这种设计确保了系统的长期可维护性和扩展性。

依赖倒置原则的应用

高层业务模块不依赖于底层存储的具体实现,二者通过配置抽象解耦。这种设计大大降低了系统的耦合度,提高了模块的可测试性和可替换性。

配置驱动的架构优势

存储策略的选择完全由外部配置控制,实现了策略与实现的彻底分离。管理员可以根据实际需求灵活选择最适合的存储方案,无需重新部署系统。

5.2 工程化优化:

条件化装配:

通过@ConditionalOnProperty@Lazy注解实现按需初始化,避免不必要的资源消耗。这种设计在微服务架构中尤为重要,能够显著提升系统的性能和资源利用率。

地址解析:

自动将域名解析为IP地址,解决了MinIO SDK的技术限制。这种"适配层"设计体现了工程化思维——不因技术框架的限制而牺牲最佳实践。

桶自初始化:

应用启动时自动创建所需存储桶,避免了因配置遗漏导致的运行时错误。这种设计大大降低了运维复杂度,确保系统能够"开箱即用"。

资源管理:

显式关闭文件流,避免临时文件残留和内存泄漏。这种资源管理方式体现了对系统稳定性的高度重视,是工程化设计的重要体现。

5.3 高可用保障:

多副本存储:

MinIO的分布式架构支持数据多副本存储,即使单个节点完全宕机,文件仍可从其他节点正常访问。这种设计将文件可用性从本地存储的约99%提升至99.9%以上。

故障转移:

MinIO集群支持自动故障转移,当某个存储节点发生故障时,系统会自动将请求路由到健康的节点。这种机制确保了服务的连续性,提升了用户体验。

横向扩展:

MinIO支持动态扩容,可以根据业务需求随时增加存储节点。这种设计确保了系统能够适应业务的快速增长,避免了频繁的架构改造。

六、总结

通过MinIO分布式存储方案的实施,我们成功解决了智慧园区项目的文件存储瓶颈,但更重要的是,这套方案体现了工程化设计的核心价值:​

文件可用性提升至99.9%以上,满足企业级应用的标准。多副本存储机制确保了数据的安全性,即使硬件发生故障,文件服务也能持续可用。​

支持PB级存储,满足海量文档需求。横向扩展能力确保了系统能够适应业务的快速增长,无需频繁进行架构改造。​

配置驱动的存储策略实现了业务逻辑与存储实现的彻底解耦。条件化装配、资源管理优化等技术细节体现了对系统性能和稳定性的高度重视。​

兼容现有本地存储,支持配置化切换。这种渐进式迁移策略大大降低了系统改造风险,确保了业务的连续性。​

桶自动创建、配置集中管理、故障自动恢复等特性大大降低了运维复杂度,符合DevOps最佳实践。

这套方案不仅解决了具体的技术问题,更体现了软件工程的系统化思维。从配置驱动的架构设计到资源管理的细节优化,每一个技术决策都经过了深思熟虑,确保了系统的长期可维护性和可扩展性。这种工程化设计能力,正是构建高质量企业级应用的关键所在。

相关完整代码:

通用请求处理

package com.ruoyi.web.controller.common;

import java.io.IOException;
import java.io.InputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.ruoyi.common.config.ServerConfig;
import com.ruoyi.common.utils.uuid.Seq;
import com.ruoyi.web.core.config.MinioConfig;
import io.minio.MinioClient;
import io.minio.errors.*;
import org.apache.commons.io.FilenameUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import com.ruoyi.common.config.RuoYiConfig;
import com.ruoyi.common.constant.Constants;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.file.FileUploadUtils;
import com.ruoyi.common.utils.file.FileUtils;
import org.xmlpull.v1.XmlPullParserException;

/**
 * 通用请求处理
 *
 * @author ruoyi
 */
@Controller
@RequestMapping("/common")
public class CommonController
{
    private static final Logger log = LoggerFactory.getLogger(CommonController.class);

    @Autowired
    private ServerConfig serverConfig;

    private static final String FILE_DELIMETER = ",";

    @Autowired
    private MinioClient minioClient;

    /**
     * 通用下载请求
     *
     * @param fileName 文件名称
     * @param delete 是否删除
     */
    @GetMapping("/download")
    public void fileDownload(String fileName, Boolean delete, HttpServletResponse response, HttpServletRequest request)
    {
        try
        {
            if (!FileUtils.checkAllowDownload(fileName))
            {
                throw new Exception(StringUtils.format("文件名称({})非法,不允许下载。 ", fileName));
            }
            String realFileName = System.currentTimeMillis() + fileName.substring(fileName.indexOf("_") + 1);
            String filePath = RuoYiConfig.getDownloadPath() + fileName;

            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            FileUtils.setAttachmentResponseHeader(response, realFileName);
            FileUtils.writeBytes(filePath, response.getOutputStream());
            if (delete)
            {
                FileUtils.deleteFile(filePath);
            }
        }
        catch (Exception e)
        {
            log.error("下载文件失败", e);
        }
    }

    private String upload2Minio(MultipartFile file){
        String name = Seq.getId(Seq.uploadSeqType)+"."+ FilenameUtils.getExtension(file.getOriginalFilename());
        InputStream inputStream = null;
        try {
            inputStream = file.getInputStream();
            minioClient.putObject(
                    MinioConfig.getBucket(), name,inputStream,file.getSize(),null,null, file.getContentType()
            );
        } catch (Exception e) {
            throw new RuntimeException(e);
        }finally {
            try {
                inputStream.close(); //minio上传完成后,要关闭文件流,否则造成临时文件占用,spring无法自动清除
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
        return name;
    }

    /**
     * 通用上传请求(单个)
     */
    @PostMapping("/upload")
    @ResponseBody
    public AjaxResult uploadFile(MultipartFile file) throws Exception
    {
        try
        {
            String fileName=null,url=null;
            // 上传并返回新文件名称
            if ("minio".equals(RuoYiConfig.getUploadType())){
                //如果指定了minio上传
                fileName = upload2Minio(file);
                url = MinioConfig.getUrl()+"/"+MinioConfig.getBucket()+"/"+fileName;
            }else{
                //默认上传本地
                String filePath = RuoYiConfig.getUploadPath();
                fileName = FileUploadUtils.upload(filePath, file);
                url = serverConfig.getUrl() + fileName;
            }
            AjaxResult ajax = AjaxResult.success();
            ajax.put("url", url);
            ajax.put("fileName", fileName);
            ajax.put("newFileName", FileUtils.getName(fileName));
            ajax.put("originalFilename", file.getOriginalFilename());
            return ajax;
        }
        catch (Exception e)
        {
            return AjaxResult.error(e.getMessage());
        }
    }

    /**
     * 通用上传请求(多个)
     */
    @PostMapping("/uploads")
    @ResponseBody
    public AjaxResult uploadFiles(List<MultipartFile> files) throws Exception
    {
        try
        {
            // 上传文件路径
            String filePath = RuoYiConfig.getUploadPath();
            List<String> urls = new ArrayList<String>();
            List<String> fileNames = new ArrayList<String>();
            List<String> newFileNames = new ArrayList<String>();
            List<String> originalFilenames = new ArrayList<String>();
            for (MultipartFile file : files)
            {
                String fileName=null,url=null;
                // 上传并返回新文件名称
                if ("minio".equals(RuoYiConfig.getUploadType())){
                    //如果指定了minio上传
                    fileName = upload2Minio(file);
                    url = MinioConfig.getUrl()+"/"+MinioConfig.getBucket()+"/"+fileName;
                }else{
                    //默认上传本地
                    fileName = FileUploadUtils.upload(filePath, file);
                    url = serverConfig.getUrl() + fileName;
                }
                urls.add(url);
                fileNames.add(fileName);
                newFileNames.add(FileUtils.getName(fileName));
                originalFilenames.add(file.getOriginalFilename());
            }
            AjaxResult ajax = AjaxResult.success();
            ajax.put("urls", StringUtils.join(urls, FILE_DELIMETER));
            ajax.put("fileNames", StringUtils.join(fileNames, FILE_DELIMETER));
            ajax.put("newFileNames", StringUtils.join(newFileNames, FILE_DELIMETER));
            ajax.put("originalFilenames", StringUtils.join(originalFilenames, FILE_DELIMETER));
            return ajax;
        }
        catch (Exception e)
        {
            return AjaxResult.error(e.getMessage());
        }
    }

    /**
     * 本地资源通用下载
     */
    @GetMapping("/download/resource")
    public void resourceDownload(String resource, HttpServletRequest request, HttpServletResponse response)
            throws Exception
    {
        try
        {
            if (!FileUtils.checkAllowDownload(resource))
            {
                throw new Exception(StringUtils.format("资源文件({})非法,不允许下载。 ", resource));
            }
            // 本地资源路径
            String localPath = RuoYiConfig.getProfile();
            // 数据库资源地址
            String downloadPath = localPath + StringUtils.substringAfter(resource, Constants.RESOURCE_PREFIX);
            // 下载名称
            String downloadName = StringUtils.substringAfterLast(downloadPath, "/");
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            FileUtils.setAttachmentResponseHeader(response, downloadName);
            FileUtils.writeBytes(downloadPath, response.getOutputStream());
        }
        catch (Exception e)
        {
            log.error("下载文件失败", e);
        }
    }
}

MinioConfig

package com.ruoyi.web.core.config;

import io.minio.MinioClient;
import io.minio.org.apache.commons.validator.routines.InetAddressValidator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;

import java.net.InetAddress;

@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {
    final static Logger logger = LoggerFactory.getLogger(MinioConfig.class);

    private static String host;
    private static String port;
    private static String bucket;
    private static String username;
    private static String password;
    private static String url;

    @Bean
    @Lazy
    @ConditionalOnProperty(name = "ruoyi.uploadType", havingValue = "minio")
    public MinioClient minioClient() throws Exception {
        //minio的初始化存在一个小问题,endpoint必须是ip形式,不能是host
        if (!InetAddressValidator.getInstance().isValid(host)){
            logger.warn("MinIO:host is not format as ip,change it!");
            InetAddress inetAddress = InetAddress.getByName(host);
            host = inetAddress.getHostAddress();
            logger.info("MinIO:host change to : {}" , host);
        }
        MinioClient minioClient = new MinioClient(host,
                Integer.valueOf(port),
                username,
                password,false);

        logger.info("minio connected, buckets="+minioClient.listBuckets());

        boolean found = minioClient.bucketExists(bucket);
        if (!found) {
            // 创建桶
            minioClient.makeBucket(bucket);
        }
        logger.info("MinIO:bucket init ok , bucket={}" , bucket);
        return minioClient;
    }


    public static String getHost() {
        return host;
    }

    public static String getPort() {
        return port;
    }

    public static String getBucket() {
        return bucket;
    }

    public static String getUsername() {
        return username;
    }

    public static String getPassword() {
        return password;
    }

    public static String getUrl() {
        return url;
    }

    public void setHost(String host) {
        MinioConfig.host = host;
    }

    public void setPort(String port) {
        MinioConfig.port = port;
    }

    public void setBucket(String bucket) {
        MinioConfig.bucket = bucket;
    }

    public void setUsername(String username) {
        MinioConfig.username = username;
    }

    public void setPassword(String password) {
        MinioConfig.password = password;
    }

    public void setUrl(String url) {
        MinioConfig.url = url;
    }
}

关键代码解释:

Logo

更多推荐