文章目录
- [一. MinIO 简介](#一. MinIO 简介)
-
- [1. 什么是MinIO?](#1. 什么是MinIO?)
- [2. 应用场景](#2. 应用场景)
- [二. 文件系统存储发展史](#二. 文件系统存储发展史)
- 三.MinIO基础概念
-
- MinIO的基础概念
- [MinIO 纠删码 EC (Erasure Code)](#MinIO 纠删码 EC (Erasure Code))
- [三. MinIO 安装指南](#三. MinIO 安装指南)
-
- [1 单机部署](#1 单机部署)
- [2 分布式集群部署](#2 分布式集群部署)
- [3 Docker 部署](#3 Docker 部署)
- [四. MinIO 基础使用](#四. MinIO 基础使用)
-
- [1. 基础命令](#1. 基础命令)
-
- [1.1 安装与配置](#1.1 安装与配置)
- [2. 存储桶管理](#2. 存储桶管理)
-
- [2.1 创建存储桶](#2.1 创建存储桶)
- [2.2 删除存储桶](#2.2 删除存储桶)
- [2.3 列出存储桶](#2.3 列出存储桶)
- [2.4 查看存储桶策略](#2.4 查看存储桶策略)
- [2.5 设置存储桶策略](#2.5 设置存储桶策略)
- [3. 对象管理](#3. 对象管理)
-
- [3.1 上传文件](#3.1 上传文件)
- [3.2 下载文件](#3.2 下载文件)
- [3.3 列出对象](#3.3 列出对象)
- [3.4 删除对象](#3.4 删除对象)
- [3.5 批量删除对象](#3.5 批量删除对象)
- [4. 高级功能](#4. 高级功能)
-
- [4.1 同步数据](#4.1 同步数据)
- [4.2 数据迁移](#4.2 数据迁移)
- [4.3 设置生命周期规则](#4.3 设置生命周期规则)
- [4.4 查看服务器信息](#4.4 查看服务器信息)
- [4.5 用户管理](#4.5 用户管理)
- [4.6 策略管理](#4.6 策略管理)
- [5. 常用快捷命令](#5. 常用快捷命令)
- [五. Spring Boot 集成 MinIO](#五. Spring Boot 集成 MinIO)
-
- [1 添加依赖](#1 添加依赖)
- [2.在SpringBoot的配置文件中编写 MinIO 的配置:](#2.在SpringBoot的配置文件中编写 MinIO 的配置:)
- [3.编写 MinIO 配置类](#3.编写 MinIO 配置类)
- [4.编写 MinIO 的工具类](#4.编写 MinIO 的工具类)
- 5.业务测试
- [六. 常见问题](#六. 常见问题)
一. MinIO 简介
1. 什么是MinIO?
MinlO是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据。例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
MinlO是一个非常轻量的服务 可以很简单的和其他应用的结合,类似NodeJS, Redis或者MySQL。
- 核心特性 :
- 支持海量非结构化数据(图片、视频、日志等)支持单个对象最大5TB。
- 轻量级(二进制文件仅数十MB)。
- 支持单机部署和分布式集群。
- 提供数据加密、版本控制、生命周期管理等企业级功能。
2. 应用场景
- 云原生存储(Kubernetes 集成)。
- 大数据存储(与 Hadoop、Spark 对接)。
- 多媒体资源存储与加速。
二. 文件系统存储发展史
1. 服务器磁盘(本地存储)
定义
- 直接挂载在服务器上的物理或虚拟磁盘(如 SSD、HDD),通过文件系统(如 ext4、NTFS)管理数据。
优点
- 低延迟:数据直接读写,无需网络开销。
- 高性能:适合频繁读写、小文件操作(如数据库事务)。
- 简单易用:无需复杂配置,开箱即用。
缺点
- 单点故障:硬盘损坏可能导致数据丢失。
- 扩展性差:受限于单机容量,无法横向扩展。
- 资源共享困难:数据无法跨服务器直接访问。
适用场景
- 单机应用(如小型网站、个人博客)。
- 高性能需求场景(如实时数据库、缓存服务)。
- 临时文件存储(如日志、临时缓存)。
2. 分布式文件系统(如 HDFS、Ceph、GlusterFS)
定义
- 通过网络将多台服务器的存储资源整合为统一命名空间,提供文件存储和共享能力(如 HDFS、Ceph、GlusterFS)。
- 它使文件可以跨越多个服务器或存储设备存储和访问。DFS 通过网络将多个存储资源组合成一个统一的文件系统,使用户和应用程序可以像访问本地文件一样透明地访问远程文件。
优点
- 横向扩展:可动态添加节点,支持 PB 级存储。
- 高可用性:数据多副本或纠删码冗余,容忍节点故障。
- 共享访问:多客户端可同时读写同一文件系统。
缺点
- 复杂度高:需管理元数据、数据分布和一致性。
- 性能瓶颈:元数据操作(如 HDFS 的 NameNode)可能成为瓶颈。
- 成本较高:需维护多节点集群,硬件和运维成本上升。
适用场景
- 大数据处理(如 Hadoop 生态、日志分析)。
- 需要共享存储的场景(如 Kubernetes 动态卷、虚拟机镜像存储)。
- 高吞吐需求(如视频流媒体、科学计算)。
3. 对象存储(如 MinIO、AWS S3)
定义
- 以对象(Object)为基本单元的存储系统,每个对象包含数据、元数据和唯一标识符(如 S3 的 Key),通过 RESTful API 访问。
优点
- 海量扩展:支持百亿级对象存储,自动管理数据分布。
- 高可用与持久性:数据跨多节点冗余(如 MinIO 的纠删码)。
- 云原生集成:兼容 S3 API,无缝对接云服务和工具链。
- 成本效益:按需付费,支持冷热数据分层存储。
缺点
- 高延迟:基于 HTTP 协议,不适合实时读写。
- 弱一致性:部分系统存在最终一致性(如 S3 的跨区域复制)。
- 文件系统语义缺失:不支持随机写入、追加操作。
适用场景
- 非结构化数据存储(如图片、视频、文档)。
- 云原生应用(如 Kubernetes 静态资源托管)。
- 大数据与 AI 数据湖(如 Spark 读取 Parquet 文件)。
- 备份与归档(如日志存储、合规性归档)。
4.对比总结
特性 | 服务器磁盘 | 分布式文件系统 | 对象存储 |
---|---|---|---|
数据模型 | 层级文件系统 | 层级文件系统 | 平坦对象(Bucket/Object) |
扩展性 | 有限(单机) | 横向扩展(多节点) | 海量扩展(自动管理) |
访问协议 | 本地文件系统 API | NFS/CIFS 或专用协议 | RESTful API(S3 兼容) |
延迟 | 极低 | 中等(受网络影响) | 较高(HTTP 开销) |
适用数据 | 结构化、小文件 | 大文件、流式数据 | 非结构化、海量数据 |
维护成本 | 低 | 高(需管理集群) | 中等(云服务免运维) |
5.选型建议
-
优先选择对象存储:
- 云原生应用或需要弹性扩展的场景。
- 存储海量非结构化数据(如用户上传文件)。
- 需要跨地域数据同步或版本控制。
-
选择分布式文件系统:
- 大数据处理(如 HDFS 与 MapReduce 结合)。
- 需要共享文件系统语义(如多个 VM 挂载同一目录)。
-
保留本地存储:
- 对延迟敏感的应用(如实时数据库)。
- 小规模或临时性存储需求。
6.示例方案
-
混合方案:
- 使用本地存储处理热数据(如 Redis 缓存),对象存储存放冷数据(如历史日志)。
- 分布式文件系统(如 Ceph)作为底层存储,提供块存储、文件存储和对象存储统一接口。
-
云原生方案:
- Kubernetes 中使用 MinIO 作为持久化存储后端,结合 S3 API 实现无状态应用的数据持久化。
通过合理选择存储方案,可显著提升系统性能、降低成本并保障数据可靠性。
特性 | 传统分布式文件系统 | MinIO |
---|---|---|
数据模型 | 层级目录结构 | 平坦命名空间(Bucket/Object) |
接口 | 专用协议(如 NFS) | S3 兼容 REST API |
扩展性 | 需手动配置 | 自动数据分布与负载均衡 |
适用场景 | 通用存储 | 云原生、大数据、AI 数据湖 |
三.MinIO基础概念
MinIO的基础概念
● Object: 存储到Minio的基本对象,如文件、字节流,Anything...
● Bucket:用来存储Object的逻辑空间。每个Bucket之间的数据是相互隔离的。对于客户端而言,就相当于一个存放文件的顶层文件夹。
● Drive: 即存储数据的磁盘,在MinIO启动时,以参数的方式传入。Minio 中所有的对象数据都会存储在Drive里。
● Set:即一组Drive的集合,分布式部署根据集群规模自动划分一 个或多个Set, 每个Set中的Drive分布在不同位置。一个对象存储在一个Set 上。(For example: {1...44} is divided into 4 sets each of size 16.)
● 一个对象存储在一 个Set上
● 一个集群划分为多个Set
● 一个Set包含的Drive数显是固定的,默认由系统根据集群规模自动计算得出
● 一个SET中的Drive尽可能分布在不同的节点上
MinIO 纠删码 EC (Erasure Code)
纠删码
(Erasure Code, EC) 是一种数据保护方法,它将数据分割成片段,生成冗余数据块,并将这些数据块存储在不同的位置,如磁盘、存储节点或其他地理位置。MinIO 采用 Reed-Solomon 纠删码实现,将对象拆分成数据块和奇偶校验块,以提高数据的冗余性和可用性。
简单来说就是可以通过数学计算,把丢失的数据进行还原,它可以将n份原始数据,增加m份数据,并能通过n+m份中的任意n份数据,还原为原始数据。
即如果有任意小于等于m份的数据失效,仍然能通过剩下的数据还原出来。
举个最简单例子就是有两个数据(d1, d2),用一个校验和y(d1 + d2 = y)即可保证即使丢失其中一个,依然可以还原数据。如丢失 d1 ,则使用 y - d2 = d1 还原,同理,d2 丢失或者y丢失,均可通过计算得出。
三. MinIO 安装指南
1 单机部署
bash
# 下载并运行(Linux/macOS)
wget https://dl.min.io/server/minio/release/linux-amd64/minio
chmod +x minio
./minio server /data --console-address :9001
- 访问管理界面:
http://localhost:9001
2 分布式集群部署
bash
# 启动4节点集群(假设4台服务器IP为192.168.1.101-104)
minio server http://192.168.1.10{1...4}/data
- 自动数据冗余:采用纠删码(Erasure Code)保障数据可靠性。
3 Docker 部署
bash
docker run -p 9000:9000 -p 9001:9001 \
-v /data:/data \
-e "MINIO_ROOT_USER=admin" \
-e "MINIO_ROOT_PASSWORD=password" \
minio/minio server /data --console-address :9001
四. MinIO 基础使用
1. 基础命令
1.1 安装与配置
-
安装 mc:
bashwget https://dl.min.io/client/mc/release/linux-amd64/mc chmod +x mc sudo mv mc /usr/local/bin/
-
设置别名(连接 MinIO 服务):
bashmc alias set <ALIAS> <ENDPOINT> <ACCESS_KEY> <SECRET_KEY>
-
示例:
bashmc alias set myminio http://localhost:9000 admin password
-
-
查看别名列表:
bashmc alias list
2. 存储桶管理
2.1 创建存储桶
bash
mc mb <ALIAS>/<BUCKET_NAME>
-
示例:
bashmc mb myminio/mybucket
2.2 删除存储桶
bash
mc rb <ALIAS>/<BUCKET_NAME>
-
示例:
bashmc rb myminio/mybucket
2.3 列出存储桶
bash
mc ls <ALIAS>
-
示例:
bashmc ls myminio
2.4 查看存储桶策略
bash
mc policy get <ALIAS>/<BUCKET_NAME>
-
示例:
bashmc policy get myminio/mybucket
2.5 设置存储桶策略
bash
mc policy set <POLICY> <ALIAS>/<BUCKET_NAME>
-
策略选项:
none
(私有)、download
(只读)、upload
(上传)、public
(公开)。 -
示例:
bashmc policy set public myminio/mybucket
3. 对象管理
3.1 上传文件
bash
mc cp <SOURCE_FILE> <ALIAS>/<BUCKET_NAME>/<TARGET_PATH>
-
示例:
bashmc cp myfile.txt myminio/mybucket/
3.2 下载文件
bash
mc cp <ALIAS>/<BUCKET_NAME>/<OBJECT_PATH> <DESTINATION>
-
示例:
bashmc cp myminio/mybucket/myfile.txt ./downloaded_file.txt
3.3 列出对象
bash
mc ls <ALIAS>/<BUCKET_NAME>
-
示例:
bashmc ls myminio/mybucket
3.4 删除对象
bash
mc rm <ALIAS>/<BUCKET_NAME>/<OBJECT_PATH>
-
示例:
bashmc rm myminio/mybucket/myfile.txt
3.5 批量删除对象
bash
mc rm --recursive --force <ALIAS>/<BUCKET_NAME>/<PREFIX>
-
示例:
bashmc rm --recursive --force myminio/mybucket/old_data/
4. 高级功能
4.1 同步数据
-
本地到远程同步:
bashmc mirror <LOCAL_DIR> <ALIAS>/<BUCKET_NAME>
-
示例:
bashmc mirror ./local_data/ myminio/mybucket/
-
-
远程到本地同步:
bashmc mirror <ALIAS>/<BUCKET_NAME> <LOCAL_DIR>
4.2 数据迁移
-
从其他 S3 兼容存储迁移到 MinIO:
bashmc mirror s3-source/ myminio/mybucket/
4.3 设置生命周期规则
bash
mc ilm add <ALIAS>/<BUCKET_NAME> --expiry-days <DAYS>
-
示例:
bashmc ilm add myminio/mybucket --expiry-days 30
4.4 查看服务器信息
bash
mc admin info <ALIAS>
-
示例:
bashmc admin info myminio
4.5 用户管理
-
添加用户:
bashmc admin user add <ALIAS> <ACCESS_KEY> <SECRET_KEY>
-
示例:
bashmc admin user add myminio newuser newpassword
-
-
删除用户:
bashmc admin user remove <ALIAS> <ACCESS_KEY>
4.6 策略管理
-
创建策略:
bashmc admin policy create <ALIAS> <POLICY_NAME> <POLICY_FILE>
-
示例:
bashmc admin policy create myminio readonly-policy policy.json
-
-
绑定策略到用户:
bashmc admin policy attach <ALIAS> <POLICY_NAME> --user=<USER>
5. 常用快捷命令
功能 | 命令 |
---|---|
查看帮助 | mc --help |
查看某个命令的帮助 | mc <COMMAND> --help |
检查配置是否正确 | mc alias test <ALIAS> |
清空存储桶 | mc rb --force <ALIAS>/<BUCKET_NAME> |
查看存储桶占用空间 | mc du <ALIAS>/<BUCKET_NAME> |
五. Spring Boot 集成 MinIO
1 添加依赖
xml
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.2</version>
</dependency>
2.在SpringBoot的配置文件中编写 MinIO 的配置:
yml
minio:
url: http://127.0.0.1:9005 #ip地址
accessKey: admin # 账号
secretKey: admin962464 # 密码
secure: false #如果是true,则用的是https而不是http,默认值是true
bucketName: "test" # 桶的名字 相当于文件夹
3.编写 MinIO 配置类
java
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {
/**
* 服务地址
*/
private String url;
/**
* 用户名
*/
private String accessKey;
/**
* 密码
*/
private String secretKey;
/**
* 存储桶名称
*/
private String bucketName;
@Bean
public MinioClient getMinioClient() {
return MinioClient.builder().endpoint(url).credentials(accessKey, secretKey).build();
}
}
4.编写 MinIO 的工具类
java
import com.jjy.shopping_file_service.config.MinioConfig;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;
@Slf4j
@Component
public class MinIOUtil {
@Resource
private MinioConfig minioConfig;
@Resource
private MinioClient minioClient;
/**
* 查看存储bucket是否存在
*
* @param bucketName 存储桶名称
* @return boolean
*/
public Boolean bucketExists(String bucketName) {
Boolean found;
try {
found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return found;
}
/**
* 创建存储bucket
*
* @param bucketName 存储桶名称
* @return Boolean
*/
public Boolean makeBucket(String bucketName) {
try {
minioClient.makeBucket(MakeBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 删除存储bucket
*
* @param bucketName 存储桶名称
* @return Boolean
*/
public Boolean removeBucket(String bucketName) {
try {
minioClient.removeBucket(RemoveBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 获取全部bucket
*
* @return 存储桶列表
*/
public List<Bucket> getAllBuckets() {
try {
return minioClient.listBuckets();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 文件上传
*
* @param file 文件
* @return 文件对象名称
*/
public String upload(MultipartFile file) {
String originalFilename = file.getOriginalFilename();
System.out.println(originalFilename);
if (!StringUtils.hasText(originalFilename)) {
throw new RuntimeException();
}
String fileName = UUID.randomUUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
String prefix = new SimpleDateFormat("yyyy/MM/dd").format(new Date());
String objectName = prefix + "/" + fileName;
try {
PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(minioConfig.getBucketName()).object(objectName)
.stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
// 文件名称相同会覆盖
minioClient.putObject(objectArgs);
} catch (Exception e) {
e.printStackTrace();
return null;
}
return objectName;
}
/**
* 预览图片
*
* @param fileName 文件名称
* @return 文件预览链接
*/
public String preview(String fileName) {
// 查看文件地址
GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs
.builder()
.bucket(minioConfig.getBucketName())
.object(fileName).method(Method.GET).build();
try {
String url = minioClient.getPresignedObjectUrl(build);
return url;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 文件下载
*
* @param fileName 文件名称
* @param res response
*/
public void download(String fileName, HttpServletResponse res) {
GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(minioConfig.getBucketName())
.object(fileName).build();
try (GetObjectResponse response = minioClient.getObject(objectArgs)) {
byte[] buf = new byte[1024];
int len;
try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
while ((len = response.read(buf)) != -1) {
os.write(buf, 0, len);
}
os.flush();
byte[] bytes = os.toByteArray();
res.setCharacterEncoding("utf-8");
res.addHeader("Content-Disposition", "attachment;fileName=" + fileName);
try (ServletOutputStream stream = res.getOutputStream()) {
stream.write(bytes);
stream.flush();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 查看文件对象
*
* @return 存储bucket内文件对象信息
*/
public List<Item> listObjects() {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder().bucket(minioConfig.getBucketName()).build());
List<Item> items = new ArrayList<>();
try {
for (Result<Item> result : results) {
items.add(result.get());
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
return items;
}
/**
* 删除
*
* @param fileName 文件名称
* @return 是否删除成功
*/
public boolean remove(String fileName) {
try {
minioClient.removeObject(RemoveObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(fileName)
.build());
} catch (Exception e) {
return false;
}
return true;
}
}
5.业务测试
java
import com.cyw.miniodemo.config.MinioConfig;
import com.cyw.miniodemo.pojo.Rst;
import com.cyw.miniodemo.service.FileUploadService;
import com.cyw.miniodemo.utils.MinIOUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;
@Slf4j
@RestController
@RequestMapping("/api/file")
@AllArgsConstructor
public class FileUploadController {
private MinioConfig minioConfig;
private MinIOUtil minIOUtil;
private FileUploadService fileUploadService;
@GetMapping("/bucketExists")
public Rst bucketExists(@RequestParam("bucketName") String bucketName) {
Map<String, Object> map = new HashMap<>();
map.put("bucketExists", minIOUtil.bucketExists(bucketName));
return Rst.ok("查询成功", map);
}
@GetMapping("/makeBucket")
public Rst makeBucket(@RequestParam("bucketName") String bucketName) {
Map<String, Object> map = new HashMap<>();
map.put("makeBucketSuccess", minIOUtil.makeBucket(bucketName));
return Rst.ok("创建成功", map);
}
@GetMapping("/removeBucket")
public Rst removeBucket(@RequestParam("bucketName") String bucketName) {
Map<String, Object> map = new HashMap<>();
map.put("deleteBucketSuccess", minIOUtil.removeBucket(bucketName));
return Rst.ok("删除成功", map);
}
@GetMapping("/getAllBuckets")
public Rst getAllBuckets() {
Map<String, Object> map = new HashMap<>();
map.put("buckets", minIOUtil.getAllBuckets());
return Rst.ok("查询成功", map);
}
@PostMapping("/upload")
public Rst upload(@RequestParam("file") MultipartFile file) {
String objectName = minIOUtil.upload(file);
if (objectName != null) {
Map<String, Object> map = new HashMap<>();
map.put("url", (minioConfig.getEndpoint() + "/" + minioConfig.getBucketName() + "/" + objectName));
return Rst.ok("上传成功", map);
}
return Rst.fail("上传失败");
}
@GetMapping("/preview")
public Rst preview(@RequestParam("fileName") String fileName) {
Map<String, Object> map = new HashMap<>();
map.put("url", minIOUtil.preview(fileName));
return Rst.ok("预览成功", map);
}
@GetMapping("/download")
public Rst download(@RequestParam("fileName") String fileName, HttpServletResponse resp) {
minIOUtil.download(fileName, resp);
return Rst.ok();
}
@PostMapping("/delete")
public Rst remove(@RequestBody Map<String, String> params) {
String url = params.get("url");
String objName = url.substring(url.lastIndexOf(minioConfig.getBucketName() + "/") + minioConfig.getBucketName().length() + 1);
log.info("删除对象: {}", objName);
minIOUtil.remove(objName);
return Rst.ok("删除成功");
}
}
六. 常见问题
- 如何扩容集群?:直接添加新节点,MinIO 自动数据再平衡。
- 数据安全性:启用 HTTPS、配置 Bucket Policy、使用 Server-Side Encryption。
- 性能优化 :调整纠删码配置(如
MINIO_STORAGE_CLASS_STANDARD=EC:4
)。