MinIo
认识MinIo
Minio是一个简单易用的云存储服务,就像是一个放在网络上的大文件柜。想象一下,你有一间放满了各种文件的房间,有时候你需要把这些文件分享给朋友或者在不同地方访问它们。Minio就是帮你做到这一点的工具,它让你可以轻松地把文件上传到互联网上,这样无论你在哪里,只要有网络,就能访问或分享这些文件。 上传文件:首先,你通过Minio提供的接口或者工具,把视频或图片上传到Minio的服务器上。上传时,你可以设置一些额外的信息,告诉Minio这些文件是可以被公开访问的。
生成链接:当你要在网站上展示这些文件时,Minio能生成一个特殊的网址(我们叫做对象URL)。这个链接直接指向存储在Minio中的视频或图片。关键在于,这个链接可以设置一个有效期,过了时间就失效,保证安全性。
前端显示:网站的前端代码(就是浏览器能看到的部分)会用这个链接去请求Minio服务器上的视频或图片。当浏览器收到这些文件时,如果它们是图片,就会直接显示在页面上;如果是视频,则会在页面上嵌入一个播放器让用户观看。
下载功能:对于下载来说,其实也很简单。因为前端已经有了这个文件的直接链接,用户点击“下载”按钮时,浏览器就会使用这个链接开始下载文件到用户的电脑上,就像平时在网上下载东西一样。
MinIo下载和启动
官网地址
代码地址
github地址:https://github.com/minio
gitee地址:https://gitee.com/mirrors/minio
下载方式(登录官网)

Linux环境下下载及启动流程
wget https://dl.minio.org.cn/server/minio/release/linux-amd64/minio #下载文件
chmod +x minio # 添加可执行权限
MINIO_ROOT_USER=admin MINIO_ROOT_PASSWORD=password ./minio server /mnt/data --console-address ":9001" # 前台启动方式
# MINIO_ROOT_USER 是指定minio的用户名
# MINIO_ROOT_PASSWORD 是指定的minio的密码
# /mnt/data 是指定的MinIO服务器用于存储数据的目录
# console-address ":9001"nohup /usr/local/java/MinIo/minio server /usr/local/java/MinIo/data > /usr/local/java/MinIo/data/minio.log 2>&1 & #后台启动
nohup作用:使进程忽略挂断信号(SIGHUP),即终端关闭时进程不会终止,保证即使你退出 SSH 连接,MinIO 服务也能继续运行,典型场景:服务器上需要长期运行的服务
/usr/local/java/MinIo/minio 作用:指定 MinIO 可执行文件的绝对路径
server /usr/local/java/MinIo/data 作用:MinIO 的命令参数,server:表示以服务器模式运行,/usr/local/java/MinIo/data:指定数据存储目录
> /usr/local/java/MinIo/data/minio.log 作用:将标准输出(stdout)重定向到日志文件
2>&1 作用:将标准错误(stderr)也重定向到标准输出 2 表示标准错误(文件描述符2) 1 表示标准输出(文件描述符1) & 表示"等同于" 合起来就是"将错误输出也发送到标准输出的位置"
& 作用:将进程放入后台运行 命令会立即返回一个进程ID,你可以继续使用当前终端,进程会在后台持续运行特别注意(有坑):MinIO 默认绑定的是 127.0.0.1(localhost),因此只能本地访问。如果部署到云服务器上,要让外部访问,需要显式绑定到服务器的 公网IP 或 0.0.0.0(所有网络接口)
nohup /usr/local/java/MinIo/minio server --address "0.0.0.0:9000" --console-address ":9001" /usr/local/java/MinIo/data > /usr/local/java/MinIo/data/minio.log 2>&1 & # 绑定IP地址进行访问
0.0.0.0 表示监听所有网络接口(包括公网、内网、localhost)
9000 是默认端口,可自定义(如 --address "0.0.0.0:9001")
--console-address ":9001" 控制台面板监听 9001 端口,: 代表监听所有接口(包括 IPv4/IPv6)特别特别注意(有坑): 如果是云服务器访问,第一需要你放行虚拟机系统防火墙的9000和9001端口;第二需要你在云服务器上放在9000和9001的安全组;第三需要你放开轻量级服务器的防火墙端口;缺一不可
服务器防火墙
系统防火墙
服务器安全组
启动成功页面

浏览器地址访问

MinIo中重要概念
MinIo 对象存储
对象存储是指将数据作为对象进行存储,采用平坦的结构,并通过对象键来唯一标识每个对象。存储对象时,MinIO不依赖文件系统,而是直接管理对象存储。
MinIO 存储桶(Bucket)
概念:存储桶通常也称为 bucket,每一个桶理论上的空间是无上限的,但是有地域的区别,如成都。在每个桶中可以创建多个目录、上传多个文件,而且每个目录中同样可以创建目录和上传文件;理论上文件数量无限,是以单一文件的形式存储的。每个文件夹或文件都可以设置权限,和 header 头,桶也可以设置权限。对于用户而言,存储桶相当于存放文件的顶层文件夹
MinIo 对象(Object)
对象是存储在桶中的最小数据单位。在MinIO中,所有的数据都是以对象的形式存储。每个对象由数据(通常是文件的内容,可以是任意类型的数据,如图像、音频、视频等)、元数据(描述对象的数据的附加信息。如文件的大小、创建日期、修改时间、权限信息等)和对象键(桶中每个对象的唯一标识,通常由文件名、路径或其他唯一标识符组成)组成。MinIO中的对象可以是任何类型的数据(文件、图片、视频、备份文件等),其大小可以从几字节到几千TB不等。相对于用户而言,对象相当于文件
SpringBoot集成MinIo
添加MinIo依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>7.1.0</version>
</dependency>添加配置信息
- 在yml中配置minio信息(配置自己对应的信息)
oss:
minio-url: http://127.0.0.1:9000
minio-access-key: minio
minio-secret-key: minio123
minio-bucket: box-data- 配置类:minioConfig
创建minioConfig来配置MinioClient的相关信息并返回,就可以在你需要使用的地方进行依赖的注入,直接使用。
MinioClient 是用于与 MinIO 服务器进行交互的客户端工具,它提供了一些命令行操作,允许用户通过 API 上传、下载、删除文件、创建存储桶等
package com.minio.config;
import io.minio.MinioClient;
import io.minio.errors.MinioException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
@Configuration
public class MinioConfig {
@Value("${oss.minio-url}")
private String minioUrl;
@Value("${oss.minio-access-key}")
private String accessKey;
@Value("${oss.minio-secret-key}")
private String secretKey;
//MinioClient对象是线程安全的
@Bean
public MinioClient minioClient() throws MinioException, IOException, NoSuchAlgorithmException {
return MinioClient.builder()
.endpoint(minioUrl)
.credentials(accessKey, secretKey)
.build();
}
}MinioClient的常用API
bucketExists():用于检查指定的存储桶是否存在,返回布尔值,表示存储桶是否存在
@SpringBootTest
class MinIoApplicationTests {
@Resource
private MinioClient minioClient;
@Test
void test01() throws Exception {
boolean bool = minioClient.bucketExists(BucketExistsArgs.builder().bucket("myfile").build());
System.out.println("测试的目录是否存在:" + bool);
}
}注意:桶的名称不能包含大写字母
makeBucket():用于创建一个新的存储桶(bucket),需要指定存储桶的名称
@Test
void test02() throws Exception {
// 没有返回值
minioClient.makeBucket(MakeBucketArgs.builder().bucket("myfile").build());
// 调用test01方法,判断该名称的桶是否存在
test01();
}
listBuckets():用于列出用户有权访问的所有存储桶,返回存储桶的列表
@Test
void test03() throws Exception {
List<Bucket> buckets = minioClient.listBuckets();
if (buckets != null) {
buckets.forEach(s -> {
System.out.println(s.name());
});
}
}removeBucket():用于删除一个已存在的存储桶(bucket),删除失败会抛出异常;
@Test
void test04() throws Exception {
// 如果该名称的桶存在,就执行删除操作
if (minioClient.bucketExists(BucketExistsArgs.builder().bucket("myfile").build())) {
// 报异常则删除失败,没有报错就删除成功
minioClient.removeBucket(RemoveBucketArgs.builder().bucket("myfile").build());
}
}putObject():用于上传文件到指定的存储桶
// 该方法需要FileInputStream 对象
@Test
void test05() throws Exception {
File file = new File("C:\\Users\\zuo\\Downloads\\若依框架代码结构图.png");
ObjectWriteResponse objectWriteResponse = minioClient.putObject(PutObjectArgs.builder()
.bucket("myfile")
.object("test.png")
.stream(new FileInputStream(file), file.length(), -1)
.build());
System.out.println(objectWriteResponse);
}uploadObject():用于上传文件到指定的存储桶
@Test
void test06() throws Exception {
ObjectWriteResponse objectWriteResponse = minioClient.uploadObject(UploadObjectArgs.builder()
.bucket("myfile") // 桶的名称
.object("test2.png") // 上传到MinIo上文件的名称
.filename("C:\\Users\\zuo\\Downloads\\若依框架代码结构图.png") // 需要上传文件的路径名称
.build());
System.out.println(objectWriteResponse);
}statObject():用于检查指定的对象(文件)的状态(存在输出文件信息,不存在抛异常)
@Test
void test07() throws Exception {
ObjectStat objectStat = minioClient.statObject(StatObjectArgs.builder()
.bucket("myfile")
.object("test2.png")
.build());
System.out.println(objectStat);
}getPresignedObjectUrl():用于生成一个对象(文件)的签名URL,以便可以通过HTTP访问
@Test
void test08() throws Exception {
String presignedObjectUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.bucket("myfile")
.object("test2.png")
.expiry(3, TimeUnit.SECONDS) // 设置文件上传在minio上的有效时间
.method(Method.GET)
.build());
System.out.println(presignedObjectUrl);
}注意:运行之后会生成一个带有签名的URL地址(例如:http://显示的IP地址/myfile/test2.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=minioadmin%2F20250410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20250410T000442Z&X-Amz-Expires=604800&X-Amz-SignedHeaders=host&X-Amz-Signature=0304b40114cd056b75569c91ac1f836de2446b43e251f444f5592fa24b3fad17),通过这个地址就可以通过浏览器直接访问MinIo中的文件;如果想直接通过地址URL不带签名访问文件,方法① 将存储桶的权限从private 修改为public

方法② 在创建桶的时候,设置访问策略;登录MinIo可以上到存储桶的访问策略为 Costom
@Test
void test() throws Exception {
String bucketName = "myfile2";
boolean isBucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (!isBucketExists) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
} else {
System.out.println("bucket已经存在,不需要创建.");
}
String policyJsonString = "{\"Version\" : \"2012-10-17\",\"Statement\":[{\"Sid\":\"PublicRead\",\"Effect\":\"Allow\",\"Principal\":{\"AWS\":\"*\"},\"Action\":[\"s3:GetObject\"],\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"]}]}";
//创建存储桶的时候,设置该存储桶里面的文件的访问策略,运行公开的读;
minioClient.setBucketPolicy(SetBucketPolicyArgs.builder()
.bucket(bucketName)
.config(policyJsonString)//json串,里面是访问策略
.build());
}getObject():用于从指定的存储桶中下载文件
@Test
void test09() throws Exception {
InputStream inputStream = minioClient.getObject(GetObjectArgs.builder()
.bucket("myfile")
.object("test2.png")
.build());
// 将 InputStream 写入本地文件
FileOutputStream out = new FileOutputStream("D:\\downloaded_example.png");
byte[] buf = new byte[1024];
int len;
while ((len = inputStream.read(buf)) != -1) {
out.write(buf, 0, len);
}
out.close();
inputStream.close();
System.out.println("文件下载完成!");
}listObjects():用于列出指定存储桶中的所有对象(文件)
@Test
void test010() throws Exception {
Iterable<Result<Item>> myfile = minioClient.listObjects(ListObjectsArgs.builder()
.bucket("myfile")
.build());
myfile.forEach(s -> {
try {
Item item = s.get();
// 获取文件的文件名
System.out.println(item.objectName());
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}removeObject():用于删除指定存储桶中的对象,需要指定存储桶名称和对象键
@Test
void test011() throws Exception {
minioClient.removeObject(RemoveObjectArgs.builder()
.bucket("myfile")
.object("test1.png")
.build());
}MinIo集群部署
纠删码模式部署
概念:纠删码(Erasure Code)简称EC,是一种数据保护方法,也是一种算法; MinIO对纠删码模式的算法进行了实现,采用Reed-Solomon code(简称RScode)纠错码将对象拆分成N/2数据和N/2奇偶校验块,Reed Solomon利用范德蒙矩阵(Vandermonde matrix)、柯西矩阵(Cauchy matrix)的特性来实现;即将数据拆分为多个数据块和多个校验块,分散存储在不同的磁盘上,即使在部分磁盘损坏或丢失的情况下,也可以通过剩余的数据块和校验块恢复出原始数据;
举个例子:现在有12块磁盘,一个对象数据会被分成6个数据块、6个奇偶校验块,你可以损坏或丢失任意6块磁盘(不管其是存放的数据块还是奇偶校验块),你仍可以从剩下的磁盘中恢复数据
启动纠删码模式
/home/zuoyou/minio server --console-address ":9001" /opt/minio/data/data{1...12} # 单机多磁盘部署
# /opt/minio/data/data{1...12} 参数命令这是一个 Bash 花括号扩展,等价于:/opt/minio/data/data1 /opt/minio/data/data2 ... /opt/minio/data/data12
表示你要使用 12 个目录(可以理解为12块磁盘)作为 MinIO 的后端存储分布式盘位。MinIO 会将对象存储为多个分片并分布在这些目录中。注意:一般直接按照命令行启动会报错误信息,如下图所示

启动报错解决思路
- 执行命令:lsblk, lsblk是Linux中的一个命令,用于列出所有可用的块设备(数据存储设备,如硬盘、闪存驱动器)的信息,如设备名称、大小、挂载点等;

- 添加一块磁盘
① 第一步:关闭虚拟机
② 第二步:点击添加硬盘(如下图所示) 
③ 将添加的磁盘格式化为xfs格式:mkfs.xfs /dev/sdb
注:mkfs:Make File System,用于格式化磁盘/分区;xfs:指定要使用的文件系统类型(XFS 是一种高性能的日志文件系统,适合大文件、大吞吐);/dev/sdb:你要格式化的磁盘设备(整个磁盘)) 

④ 将磁盘挂载到minio的存储目录:mount /dev/sdb /opt/minio/data
注:mount:挂载命令,把设备关联到一个目录 ;/dev/sdb:要挂载的磁盘或分区(前提是已经格式化好,例如用 mkfs.xfs /dev/sdb);/opt/minio/data:挂载点,是你希望访问磁盘内容的目录。

⑤ 启动纠删码模式:/home/zuoyou/minio server --console-address ":9001" /opt/minio/data/data{1...12} # 启动命令

⑥ 页面展示效果 一个服务,12个设备;当上传一个文件时,底层会将这个文件拆开存储在这12个设备上;当某个磁盘块数据丢失了,后台会根据其他磁盘块的信息重新将丢失的磁盘块中的数据组装回来
、
启动纠删码模式(后台启动)
nohup /home/zuoyou/minio server --console-address ":9001" /opt/minio/data/data{1...12} > /opt/minio/data/minio.log 2>&1 &
nohup: 这是一个Unix命令,用于运行另一个命令在后台,并且忽略挂起(HUP)信号,也就是即使你退出了终端或关闭了会话,该命令也会继续运行;
> /opt/minio/data/minio.log: 这部分是将标准输出(stdout)重定向到 /opt/minio/data/minio.log 文件,这意味着 MinIO 服务器的所有正常输出(如启动信息、状态更新等)都会被写入到这个日志文件中;
2>&1: 这部分是将标准错误输出(stderr)重定向到标准输出(stdout),即输出到 /opt/minio/data/minio.log 文件,这样,无论是标准输出还是错误输出,都会被写入到同一个日志文件中;
&: 这个符号是在命令的末尾,用于将命令放到后台执行,也就是即使你启动了 MinIO 服务器,你的终端或 shell 会话也不会被阻塞,你可以继续执行其他命令;