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 适用场景界定
-
私有化部署场景:企业对数据私密性要求较高(如合同文档、内部研发资料),需将数据存储在自有服务器集群,MinIO 可通过分布式部署实现数据本地化管控。
-
业务数据存储场景:电商平台商品图片、社交 APP 用户头像、教育平台课程视频等非结构化数据存储,MinIO 支持高并发读写,适配业务流量波动。
-
开发测试场景:开发者需快速搭建轻量化存储服务用于功能测试,MinIO 单机部署仅需 1 个可执行文件,5 分钟内即可完成环境搭建。
-
数据备份场景:企业核心业务数据需异地备份,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.exe、mc.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 核心概念解析
-
桶(Bucket) :MinIO 的基本存储单元,用于对对象进行分类管理,类似文件系统中的 "文件夹",但无层级嵌套限制(可通过对象名中的 "/" 模拟层级)。
命名规则:3-63 个字符,仅支持小写字母、数字、横线,且不能以横线开头或结尾。
-
对象(Object) :MinIO 中存储的核心数据单元,对应实际的文件(如图片、文档),由 "文件内容 + 元数据" 组成。
元数据包含文件大小、类型、创建时间等基础信息,支持自定义扩展(如给文件添加业务标签)。
-
API 服务端口:默认 9000 端口,用于客户端(如 Spring Boot 应用)通过 SDK 集成 MinIO,实现文件上传、下载等自动化操作。
-
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. 测试验证
- 启动 SpringBoot 项目和 MinIO 服务。
- 使用 Postman 测试接口:
- 上传文件:发送 POST 请求到 http://localhost:8080/minio/upload,参数类型 form-data,键 file,值选择本地文件,返回文件访问路径。
- 下载文件:发送 GET 请求到 http://localhost:8080/minio/download?fileName=上传返回的文件名,浏览器自动下载文件。
- 删除文件:发送 DELETE 请求到 http://localhost:8080/minio/delete?fileName=文件名,返回删除结果。
- 获取临时链接:发送 GET 请求到 http://localhost:8080/minio/getUrl?fileName=文件名,返回有效期 1 小时的临时访问链接。