SpringBoot教程(三十三)| SpringBoot集成MinIO

SpringBoot教程(三十三)| SpringBoot集成MinIO

  • [一、MinIO 是什么?](#一、MinIO 是什么?)
  • [二、MinIO 与主流存储方案的对比分析](#二、MinIO 与主流存储方案的对比分析)
  • [三、MinIO 适用场景界定](#三、MinIO 适用场景界定)
  • [四、Windows 环境下 MinIO 部署与配置](#四、Windows 环境下 MinIO 部署与配置)
    • [1. 下载 MinIO](#1. 下载 MinIO)
    • [2. 启动 MinIO](#2. 启动 MinIO)
    • [3. 汉化操作](#3. 汉化操作)
    • [4. MinIO 核心概念解析](#4. MinIO 核心概念解析)
    • [5. MinIO 基础操作指南(Web 控制台)](#5. MinIO 基础操作指南(Web 控制台))
  • 五、SpringBoot集成MinIO
    • [1. 引入maven](#1. 引入maven)
    • [2. 配置 MinIO 连接信息](#2. 配置 MinIO 连接信息)
    • [3. 创建 MinIO 客户端配置类](#3. 创建 MinIO 客户端配置类)
    • [4. 封装 MinIO 工具类](#4. 封装 MinIO 工具类)
    • [5. 编写测试接口](#5. 编写测试接口)
    • [6. 测试验证](#6. 测试验证)

一、MinIO 是什么?

MinIO 是一款基于对象存储模型的高性能开源存储方案,采用 Go 语言开发,具备轻量部署、高可用、可扩展等核心特性。

其核心价值在于为用户提供私有化对象存储服务------通过将本地磁盘或服务器存储资源转化为标准化的对象存储服务,实现对图片、视频、文档等非结构化数据的高效管理,同时支持 API 集成、权限管控、数据备份等企业级能力,兼顾个人测试与生产环境部署需求。

二、MinIO 与主流存储方案的对比分析

为明确 MinIO 的适用场景,以下从存储模型、部署方式、核心优势等维度,将其与常见存储方案进行对比:

存储方案 存储模型 部署模式 核心优势 适用场景 局限性
MinIO 对象存储 单机/分布式 轻量部署、高可用、API 兼容 S3、私有化可控 企业私有化文件存储、业务数据备份、开发测试环境 需自行维护服务器硬件与部署环境
本地磁盘存储(Ext4/NTFS) 文件系统 单机本地 部署零成本、操作直观 个人临时存储、单节点小型工具 无共享能力、扩容受限、数据可靠性低
公有云对象存储(OSS/COS) 对象存储 公有云托管 零运维、弹性扩容、按需付费 无私有化需求的互联网业务、轻量级应用 数据归属第三方、长期使用成本较高
分布式文件系统(FastDFS) 文件系统 集群部署 高并发访问支持、适合大文件存储 短视频平台、海量日志存储等超大规模场景 部署复杂度高、运维成本高、适配门槛高

三、MinIO 适用场景界定

  1. 私有化部署场景:企业对数据私密性要求较高(如合同文档、内部研发资料),需将数据存储在自有服务器集群,MinIO 可通过分布式部署实现数据本地化管控。

  2. 业务数据存储场景:电商平台商品图片、社交 APP 用户头像、教育平台课程视频等非结构化数据存储,MinIO 支持高并发读写,适配业务流量波动。

  3. 开发测试场景:开发者需快速搭建轻量化存储服务用于功能测试,MinIO 单机部署仅需 1 个可执行文件,5 分钟内即可完成环境搭建。

  4. 数据备份场景:企业核心业务数据需异地备份,MinIO 支持跨节点数据同步与多副本存储,保障数据冗余安全。

四、Windows 环境下 MinIO 部署与配置

1. 下载 MinIO

硬件要求:Windows 7 及以上 64 位系统,建议 CPU 2 核及以上、内存 4GB 及以上,存储盘预留至少 10GB 可用空间(用于存储服务数据)。

软件下载:(推荐方式二)

  • 方式一:访问 MinIO 官方下载页(https://min.io/download)(太慢了),
    下载核心服务端程序 minio.exe;若需命令行管理工具,可同步下载客户端 mc.exe(可选)。
  • 方式二:访问 MinIO 国内镜像地址(https://www.minio.org.cn/)(速度飞起)
    下载核心服务端程序 minio.exe;若需命令行管理工具,可同步下载客户端 mc.exe(可选)。

2. 启动 MinIO

(1)规划目录

首先在本地磁盘(建议非系统盘,如 D 盘)创建标准化目录结构,用于统一管理程序与数据:

以下是我存放的位置(你们可以根据情况自行更改)

  • 主目录:D:\Develop\Env\MinIO(存放部署相关文件)
  • 程序目录:D:\Develop\Env\MinIO\bin(存放 minio.exemc.exe 等可执行文件)
  • 数据目录:D:\Develop\Env\MinIO\data(用于存储 MinIO 管理的对象数据,避免临时目录导致数据丢失)
  • 日志目录:D:\Develop\Env\MinIO\logs(用于存储服务启动日志,便于问题排查)


(2)创建启动脚本

创建一个txt文件,然后复制下面的命令(脚本中的位置根据你的存放的位置自行修改),在改成bat后缀

bash 复制代码
@echo off
:: 切换编码为 UTF-8,避免中文乱码
chcp 65001 > nul
echo 正在启动MinIO服务...
:: 切换至程序目录
cd D:\Develop\Env\MinIO\bin
:: 启动服务,指定数据目录、控制台端口
minio.exe server D:\Develop\Env\MinIO\data --console-address ":9001"
pause

然后放到 和 bin 目录同级别

(3)双击启动

访问 http://127.0.0.1:9001,用户名和密码默认都为 minioadmin

3. 汉化操作

目前没有汉化配置。有的话也是和源码相关的(忒麻烦),所以最快的方式就是浏览器翻译

4. MinIO 核心概念解析

  1. 桶(Bucket) :MinIO 的基本存储单元,用于对对象进行分类管理,类似文件系统中的 "文件夹",但无层级嵌套限制(可通过对象名中的 "/" 模拟层级)。

    命名规则:3-63 个字符,仅支持小写字母、数字、横线,且不能以横线开头或结尾。

  2. 对象(Object) :MinIO 中存储的核心数据单元,对应实际的文件(如图片、文档),由 "文件内容 + 元数据" 组成。

    元数据包含文件大小、类型、创建时间等基础信息,支持自定义扩展(如给文件添加业务标签)。

  3. API 服务端口:默认 9000 端口,用于客户端(如 Spring Boot 应用)通过 SDK 集成 MinIO,实现文件上传、下载等自动化操作。

  4. Web 控制台端口:默认 9001 端口,提供可视化管理界面,支持手动创建桶、上传文件、配置权限等操作。

5. MinIO 基础操作指南(Web 控制台)

(1)创建存储桶

登录 Web 控制台后,点击左侧菜单栏 "Create Bucket"。输入桶名称(如 test-123


你可以看看data下面,就出现了你创建的那个桶了

(2)对象操作---桶下面创建文件夹

前提是你创建文件夹以后,还得再到这个新文件夹里面上传一个资源才行,不然就属于无效



(3)对象操作---桶下面创建文件

你可以看看data下面,就出现了《API命名规范.txt》的这个文件夹

和windows的不一样,windows 就直接是文件了,而它是以文件夹方式的,里面还有一层,这个 xl.meta 才是《API命名规范.txt》的真正的数据

五、SpringBoot集成MinIO

1. 引入maven

我这边是jdk1.8 建议选择 8.5.6

bash 复制代码
<!-- MinIO 官方 Java SDK -->
<dependency>
    <groupId>io.minio</groupId>
    <artifactId>minio</artifactId>
    <version>8.5.6</version> <!-- JDK 1.8 推荐版本 -->
</dependency>

<!-- 用于文件流处理的工具类(可选) -->
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.11.0</version>
</dependency>

2. 配置 MinIO 连接信息

在 application.yml 中配置 MinIO 连接参数:

yml 复制代码
spring:
  minio:
    endpoint: http://localhost:9000  # MinIO API 地址
    access-key: minioadmin          # 管理员账号
    secret-key: minioadmin          # 管理员密码
    bucket-name: test-bucket        # 默认操作的桶(提前创建)
    base-url: http://localhost:9000/test-bucket/  # 公开桶的文件访问基础路径

3. 创建 MinIO 客户端配置类

bash 复制代码
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MinioConfig {

    // 从配置文件读取 MinIO 连接参数
    @Value("${spring.minio.endpoint}")
    private String endpoint;
    @Value("${spring.minio.access-key}")
    private String accessKey;
    @Value("${spring.minio.secret-key}")
    private String secretKey;

    /**
     * 初始化 MinIO 客户端,交给 Spring 容器管理
     * @return MinioClient 实例
     */
    @Bean
    public MinioClient minioClient() {
        return MinioClient.builder()
                .endpoint(endpoint)  // 设置 API 地址
                .credentials(accessKey, secretKey)  // 设置账号密码
                .build();
    }
}

4. 封装 MinIO 工具类

bash 复制代码
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.TimeUnit;

@Component
public class MinioUtil {

    @Autowired
    private MinioClient minioClient;

    @Value("${spring.minio.bucket-name}")
    private String defaultBucket;  // 默认桶名
    @Value("${spring.minio.base-url}")
    private String baseUrl;        // 公开桶的文件访问基础路径

    /**
     * 创建桶(如果桶不存在)
     * @param bucketName 桶名
     * @throws Exception 异常
     */
    public void createBucket(String bucketName) throws Exception {
        // 判断桶是否存在
        if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
            // 不存在则创建桶
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        }
    }

    /**
     * 上传文件(支持 MultipartFile 类型,Spring 接收前端文件的标准类型)
     * @param file 前端上传的文件
     * @param bucketName 桶名(可为空,使用默认桶)
     * @return 文件访问路径
     * @throws Exception 异常
     */
    public String uploadFile(MultipartFile file, String bucketName) throws Exception {
        // 若未指定桶名,使用默认桶
        if (bucketName == null || bucketName.isEmpty()) {
            bucketName = defaultBucket;
        }
        // 确保桶存在
        createBucket(bucketName);

        // 生成唯一文件名(前缀+时间戳+原文件名,避免重复)
        String originalFilename = file.getOriginalFilename();
        String fileName = "upload/" + System.currentTimeMillis() + "_" + originalFilename;

        // 上传文件到 MinIO
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName)  // 桶名
                        .object(fileName)   // 存储在 MinIO 中的文件名(可包含路径,如 "img/123.jpg")
                        // 文件流(MultipartFile 的输入流)
                        .stream(file.getInputStream(), file.getSize(), -1)
                        .contentType(file.getContentType())  // 文件类型(如 image/jpeg)
                        .build()
        );

        // 返回文件访问路径(公开桶可直接访问)
        return baseUrl + fileName;
    }

    /**
     * 下载文件(返回文件输入流,用于前端下载)
     * @param fileName 文件名(与上传时的 fileName 一致)
     * @param bucketName 桶名(可为空,使用默认桶)
     * @return 文件输入流
     * @throws Exception 异常
     */
    public InputStream downloadFile(String fileName, String bucketName) throws Exception {
        if (bucketName == null || bucketName.isEmpty()) {
            bucketName = defaultBucket;
        }
        // 获取文件输入流
        return minioClient.getObject(
                GetObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .build()
        );
    }

    /**
     * 删除文件
     * @param fileName 文件名
     * @param bucketName 桶名(可为空,使用默认桶)
     * @throws Exception 异常
     */
    public void deleteFile(String fileName, String bucketName) throws Exception {
        if (bucketName == null || bucketName.isEmpty()) {
            bucketName = defaultBucket;
        }
        // 删除文件
        minioClient.removeObject(
                RemoveObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .build()
        );
    }

    /**
     * 获取桶列表
     * @return 桶列表
     * @throws Exception 异常
     */
    public List<Bucket> getBucketList() throws Exception {
        return minioClient.listBuckets();
    }

    /**
     * 获取私有文件的临时访问链接(适用于私有桶,链接有有效期)
     * @param fileName 文件名
     * @param bucketName 桶名(可为空,使用默认桶)
     * @param expireSeconds 链接有效期(秒)
     * @return 临时访问链接
     * @throws Exception 异常
     */
    public String getPresignedUrl(String fileName, String bucketName, int expireSeconds) throws Exception {
        if (bucketName == null || bucketName.isEmpty()) {
            bucketName = defaultBucket;
        }
        // 生成临时访问链接
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)  // 请求方法
                        .bucket(bucketName)
                        .object(fileName)
                        .expiry(expireSeconds, TimeUnit.SECONDS)  // 链接有效期
                        .build()
        );
    }
}

5. 编写测试接口

bash 复制代码
import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;

@RestController
@RequestMapping("/minio")
public class MinioController {

    @Autowired
    private MinioUtil minioUtil;

    /**
     * 测试上传文件
     * @param file 前端上传的文件(form-data 格式)
     * @return 文件访问路径
     */
    @PostMapping("/upload")
    public String uploadFile(@RequestParam("file") MultipartFile file) {
        try {
            // 上传到默认桶,返回文件访问路径
            return minioUtil.uploadFile(file, null);
        } catch (Exception e) {
            return "上传失败:" + e.getMessage();
        }
    }

    /**
     * 测试下载文件
     * @param fileName 文件名(与上传时返回的路径中的文件名部分一致)
     * @return 响应实体(文件字节流)
     */
    @GetMapping("/download")
    public ResponseEntity<byte[]> downloadFile(@RequestParam("fileName") String fileName) {
        try {
            // 获取文件输入流
            InputStream in = minioUtil.downloadFile(fileName, null);
            // 转换为字节数组
            byte[] bytes = IOUtils.toByteArray(in);
            // 设置响应头(解决中文文件名乱码)
            HttpHeaders headers = new HttpHeaders();
            headers.setContentDispositionFormData("attachment", new String(fileName.getBytes("UTF-8"), "ISO-8859-1"));
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            // 返回文件字节流
            return new ResponseEntity<>(bytes, headers, HttpStatus.OK);
        } catch (Exception e) {
            return ResponseEntity.status(500).body(("下载失败:" + e.getMessage()).getBytes());
        }
    }

    /**
     * 测试删除文件
     * @param fileName 文件名
     * @return 操作结果
     */
    @DeleteMapping("/delete")
    public String deleteFile(@RequestParam("fileName") String fileName) {
        try {
            // 删除文件
            minioUtil.deleteFile(fileName, null);
            return "删除成功";
        } catch (Exception e) {
            return "删除失败:" + e.getMessage();
        }
    }

    /**
     * 测试获取私有文件的临时访问链接
     * @param fileName 文件名
     * @return 临时访问链接(有效期 1 小时)
     */
    @GetMapping("/getUrl")
    public String getFileUrl(@RequestParam("fileName") String fileName) {
        try {
            // 获取临时链接,有效期 3600 秒(1 小时)
            return minioUtil.getPresignedUrl(fileName, null, 3600);
        } catch (Exception e) {
            return "获取链接失败:" + e.getMessage();
        }
    }
}

6. 测试验证

  1. 启动 SpringBoot 项目和 MinIO 服务。
  2. 使用 Postman 测试接口:
相关推荐
像风一样自由20202 天前
Redis与MinIO:两大存储利器的区别与联系
数据库·redis·缓存·minio
chen_note9 天前
Ceph常用的三种存储操作
ceph·对象存储·minio·块存储·文件存储
橙-极纪元9 天前
文件存储服务有哪些?他们优缺点分别是什么?FastDFS、MinIO、Ceph、HDFS、MooseFS、TFS、七牛云、阿里云 OSS
hdfs·minio·七牛云·tfs·fastdfs·阿里云 oss·文件存储服务
分布式存储与RustFS18 天前
RustFS:MinIO的“平替”还是“乱杀”?
python·rust·对象存储·minio·存储·企业存储·rustfs
magic3341656324 天前
Springboot整合MinIO文件服务(windows版本)
windows·spring boot·后端·minio·文件对象存储
分布式存储与RustFS25 天前
存算一体架构的先行者:RustFS在异构计算环境下的探索与实践
大数据·人工智能·物联网·云原生·对象存储·minio·rustfs
清静诗意1 个月前
在 Ubuntu 上安装 MinIO 并使用 Python 封装类操作对象存储
服务器·minio
分布式存储与RustFS1 个月前
告别复杂配置:用Milvus、RustFS和Vibe Coding,60分钟DIY专属Chatbot
wpf·文件系统·milvus·对象存储·minio·rustfs·vibe
分布式存储与RustFS1 个月前
告别手动配置:用 Terraform 定义你的 RustFS 存储帝国
云原生·wpf·文件系统·terraform·对象存储·minio·rustfs