文章目录
- 简介
- 官方地址
- Linux下载安装
- 安装服务
- 启动
- 关闭
- 帮助命令
- java开发minio
- 依赖包
- 新建项目
- pom
- 配置文件
- 配置类
- Service
- 测试类
- 运行测试
- Api使用前言
- 针对桶的操作
- 查看某个桶是否存在
- 创建一个桶
- 返回桶列表
- 删除一个桶
- 针对文件的操作
- 上传文件到桶中(本地文件上传)
- 上传文件到桶中(基于io流上传)
- Controller上传文件到minio
- 检查某文件的状态
- 生成文件访问的URL
- 从指定的桶中下载文件
- 列出桶中所有文件
- 删除指定桶中的文件
- 纠删码模式单机部署
- 纠删码介绍
- 单机多磁盘部署准备
- 纠删码模式启动
简介
MinIO 是使用 Go (Golang) 开发的一个高性能的开源对象存储服务,专为处理海量非结构化数据(如图片、视频、日志文件、备份等)而设计,且兼容 Amazon S3 的 API。这使得它可以与现有的 S3 客户端、工具和应用程序集成。以下是 MinIO 的详细介绍:
MinIO 的主要特点
- S3 兼容:MinIO 完全兼容 Amazon S3 API,任何支持 S3 的客户端和应用都可以无缝切换到 MinIO。
- 高性能:MinIO 可以处理大规模的对象存储需求,能够提供非常高的吞吐量。它使用高效的内存管理和网络层,适合数据密集型应用程序,如大数据、AI/ML、日志分析等。
- 轻量级设计:MinIO 的二进制文件仅几十 MB,且非常轻量级。它可以在本地计算机、虚拟机、容器和 Kubernetes 等多种环境中轻松部署和运行。
- 分布式架构:MinIO 支持分布式存储,可以将多个服务器或节点组成集群,实现数据的冗余、容错和高可用性。
- 企业级功能:
- 多租户:MinIO 支持多租户架构,可将一个集群拆分为多个租户,满足复杂的权限管理和隔离需求。
- 对象锁定:支持对象的不可变性设置,适合合规性存储需求,如金融和医疗等领域的数据存档。
- 数据加密:支持静态和传输中数据的加密,确保数据安全。
- 纠删码:MinIO 使用纠删码技术来分散和保护数据,提供高度的容错能力,同时减少存储开销。
架构与技术细节
- 单节点与多节点模式:MinIO 可以在单节点部署,也支持多节点、分布式部署模式。在分布式模式下,数据会自动跨多个节点进行复制或通过纠删码保护,保证系统的高可用性。
- 高扩展性:MinIO 的分布式模式使得它能够在大规模对象存储中处理数十亿个对象。它的设计能够动态扩展,并且提供了线性扩展能力。
- 对象锁定与生命周期管理:MinIO 支持对象的生命周期策略和锁定机制,允许定义数据存储的保留期和删除策略,适用于日志、备份等长期存储需求。
MinIO 的典型应用场景
- 私有云存储:许多企业使用 MinIO 构建内部的私有云对象存储,替代昂贵的公共云存储解决方案。
- 大数据分析和机器学习:MinIO 高性能的数据传输能力非常适合需要处理大量数据的分析平台和 AI/ML 项目。
- 备份和恢复:由于其高度可靠的存储和高效的数据保护功能,MinIO 常用于备份、归档和恢复数据的解决方案。
- 混合云与多云架构:MinIO 的轻量级和兼容性使它能够轻松部署在多个云环境中,支持多云架构。
MinIO 的部署方式
- 容器化部署:MinIO 可通过 Docker 容器部署,简单且快速。
- Kubernetes 集成:MinIO 可以与 Kubernetes 集成,支持动态存储卷、持久化存储和自动扩展。
使用 MinIO 的好处
- 简化存储管理:MinIO 的 S3 API 兼容性和简单的架构设计使得它能够快速集成到现有的基础设施中,减少管理复杂度。
- 降低存储成本:作为开源软件,MinIO 没有许可费用,且其轻量和高效的特性可以减少基础设施开销。
- 灵活性:MinIO 支持多种部署方式,用户可以根据需要灵活选择在本地、私有云或多云环境中部署。
MinIO发布了以下软件开发工具包(SDK), 用于通过Api操作minio
- Go
- Python
- Java
- .NET
- JavaScript
- Haskell
- C++
官方地址
官网地址
英文:https://min.io/
中文:https://www.minio.org.cn/
下载地址
https://min.io/download?license=agpl&platform=docker
Linux下载安装
Centos7下载安装minio
访问下载地址
https://min.io/download?license=agpl&platform=linux
先下载Server,再下载客户端
安装服务
linux版本下载方法如下图,使用Binary或RPM的命令下载即可:
这里演示使用Binary二进制,执行上图命令中的wget
安装命令:
wget https://dl.min.io/server/minio/release/linux-amd64/minio
后面两条命令分别为修改权限与启动minio服务:
chmod +x minio
MINIO_ROOT_USER=admin MINIO_ROOT_PASSWORD=password ./minio server /mnt/data --console-address ":9001"
开始执行安装下载
创建新目录
[root@Centos7 ~]# mkdir /home/minio
下载
[root@Centos7 ~]# cd /home/minio
[root@Centos7 minio]# wget https://dl.min.io/server/minio/release/linux-amd64/minio
--2024-09-18 13:58:35-- https://dl.min.io/server/minio/release/linux-amd64/minio
正在解析主机 dl.min.io (dl.min.io)... 178.128.69.202, 138.68.11.125
正在连接 dl.min.io (dl.min.io)|178.128.69.202|:443... 已连接。
已发出 HTTP 请求,正在等待回应... 200 OK
长度:103944344 (99M) [application/octet-stream]
正在保存至: “minio”
100%[=========================================================================================================================================>] 103,944,344 195KB/s 用时 4m 13s
2024-09-18 14:02:49 (401 KB/s) - 已保存 “minio” [103944344/103944344])
改权限并查看
[root@Centos7 minio]# chmod +x minio
[root@Centos7 minio]# ll
总用量 131008
-rwxr-xr-x. 1 root root 103944344 9月 14 20:53 minio
启动
启动minio服务命令解释
MINIO_ROOT_USER=admin MINIO_ROOT_PASSWORD=password ./minio server /mnt/data --console-address ":9001" --address ":9000" &
或者
./minio server /mnt/data --console-address ":9001" --address ":9000" &
MINIO_ROOT_USER
:指定访问minio的用户名
MINIO_ROOT_PASSWORD
:指定访问minio的密码
./minio server
:启动minio服务,./
代表当前处于存放minio的目录位置,如果在其他目录执行启动,则使用minio存放目录/minio server
即可
/mnt/data
:指定minio服务器用于存储数据的目录
--console-address ":9001"
:指定minio控制台的监听地址和端口,冒号不能去掉,它相当于省略了ip,即可以使用任何ip访问minio服务
--address ":9000"
:指定用于数据API服务的端口,比如java连接minio进行操作时使用此端口
&
:后台启动
执行启动
出现黄色信息时,按下回车即可出现[root@Centos7 minio]#
,并且不会停止minio服务。然后记得开放防火墙的9000与9001端口:
firewall-cmd --zone=public --add-port=9000/tcp --permanent
:开放9000端口firewall-cmd --reload
:重启防火墙
[root@Centos7 minio]# firewall-cmd --zone=public --add-port=9000/tcp --permanent
success
[root@Centos7 minio]# firewall-cmd --reload
success
[root@Centos7 minio]#
开放防火墙端口后后访问上面图片信息中的WebUI
来访问控制台页面(API地址用于客户端调用访问,比如java操作minio):
http://192.168.137.200:9001
使用命令中的用户名密码admin
与password
登录:
关闭
关闭
minio
服务
先查minio服务状态:ps -ef|grep minio
,如下信息即为minio服务正在运行
[root@Centos7 minio]# ps -ef|grep minio
root 44109 44055 0 14:47 pts/0 00:00:00 ./minio server /mnt/data --console-address :9001
root 44810 44055 0 14:54 pts/0 00:00:00 grep --color=auto minio
关闭minio服务,只需要kill
杀掉上面的进程即可,进程号使用第一行第一个端口号:
[root@Centos7 minio]# kill 44109
INFO: Exiting on signal: TERMINATED
[1]+ 完成 MINIO_ROOT_USER=admin MINIO_ROOT_PASSWORD=password ./minio server /mnt/data --console-address ":9001"
再查看,minio已关闭:
[root@Centos7 minio]# ps -ef|grep minio
root 45035 44055 0 14:56 pts/0 00:00:00 grep --color=auto minio
帮助命令
执行./minio
,查看启动帮助命令:
[root@Centos7 ~]# ./minio
NAME:
minio - High Performance Object Storage
DESCRIPTION:
Build high performance data infrastructure for machine learning, analytics and application data workloads with MinIO
USAGE:
minio [FLAGS] COMMAND [ARGS...]
COMMANDS:
server start object storage server
FLAGS:
--certs-dir value, -S value path to certs directory (default: "/root/.minio/certs")
--quiet disable startup and info messages
--anonymous hide sensitive information from logging
--json output logs in JSON format
--help, -h show help
--version, -v print the version
VERSION:
RELEASE.2024-09-13T20-26-02Z
如上,启动命令格式为:minio [FLAGS] COMMAND [ARGS...]
,其中FLAGS
与ARGS
为可选,可不填
USAGE,使用的启动命令格式
格式为:minio [FLAGS] COMMAND [ARGS...]
,其中FLAGS
与ARGS
为可选,可不填
COMMANDS,对应
minio [FLAGS] COMMAND [ARGS...]
中的COMMAND
只有一个server
,代表启动对象存储服务
FLAGS可用的命令如下
对应上面的命令输出:
--certs-dir value, -S value path to certs directory (default: "/root/.minio/certs")
--quiet disable startup and info messages
--anonymous hide sensitive information from logging
--json output logs in JSON format
--help, -h show help
--version, -v print the version
java开发minio
依赖包
在官网文档出找到
SDK
–>java快速指南
,可以找到maven依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.12</version>
</dependency>
新建项目
建立一个
springboot
的项目
项目结构
pom
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>minio-demo</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.10</version>
</parent>
<dependencies>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.12</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置文件
application.yaml
server:
port: 8080
#设置文件上传大小
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
#自定义minio配置
minio:
endpoint: http://192.168.137.200:9000 #你的minio地址
accessKey: admin
secretKey: password
配置类
读取yaml的MinioProperties
package com.minio.config;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "minio")
@Data
public class MinioProperties {
private String endpoint;
private String accessKey;
private String secretKey;
}
配置minio客户端
package com.minio.config;
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MinioConfig {
@Autowired
private MinioProperties minioProperties;
//单例下的 MinioClient 是线程安全的,可以多个请求使用同一个客户端来调用
@Bean
public MinioClient minioClient(){
return MinioClient.builder()
.endpoint(minioProperties.getEndpoint()) //地址
.credentials(minioProperties.getAccessKey(),minioProperties.getSecretKey()) //用户名密码
.build();
}
}
Service
package com.minio.service;
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MinioService {
@Autowired
private MinioClient minioClient;
public void testMinio(){
System.out.println(minioClient);
}
}
测试类
package com.minio;
import com.minio.service.MinioService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import javax.annotation.Resource;
/**
* 要与上面的主启动类处于同名包内
*
* */
@SpringBootTest
public class MinioTest {
@Autowired
private MinioService minioService;
@Test
public void testOne(){
minioService.testMinio();
}
}
运行测试
运行测试方法testOne,输出MinioClient:
io.minio.MinioClient@26c89563
Api使用前言
先介绍
minio
中的Bucket
、Object
,代码中主要操作的就是二者
Bucket
:桶,是存储Object的逻辑空间,每个Bucket之间的数据是相互隔离的,对用户而言,相当于存放文件的顶层文件夹
Object
:对象,是存放到minio的基本对象,对用户而言,相当于文件
可能遇到的问题
如果出现报错:
error occurred
ErrorResponse(code = RequestTimeTooSkewed, message = The difference between the request time and the server's time is too large.
原因是因为客户端与minio所在服务器时间差距过大导致。服务端linux同步北京时间的方法,安装ntpdate
:
yum -y install ntp ntpdate
安装后执行同步命令:
ntpdate cn.pool.ntp.org
执行后用date查看当前时间:
[root@Centos7 minio]# ntpdate cn.pool.ntp.org
19 Sep 10:31:39 ntpdate[24198]: step time server 84.16.73.33 offset -0.534266 sec
[root@Centos7 minio]# date
2024年 09月 19日 星期四 10:31:42 CST
其他注意点
重复上传到一个桶的文件会被覆盖,即将同名文件多次上传到同一个桶中,会将原来的文件覆盖
针对桶的操作
查看某个桶是否存在
MinioClient的
bucketExists()
方法,用于查看某个桶是否存在,返回布尔值:true-存在,false-不存在
示例代码:
package com.minio;
import io.minio.BucketExistsArgs;
import io.minio.MinioClient;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
/**
* 要与上面的主启动类处于同名包内
*
* */
@SpringBootTest
public class MinioTestDemo {
@Autowired
private MinioClient Client;
// bucketExists方法,判断某个桶是否存在
@Test
public void testBucketExists() throws Exception {
boolean exists = Client.bucketExists(
BucketExistsArgs.builder()
.bucket("mybucket") //参数为桶的名字,需要小写,可包含数字
.build());
System.out.println(exists);
}
}
如果你没有建立过名为mybucket
的桶,则输出结果为false
。这里注意桶名字英文要小写,.bucket("mybucket")
内部有正则表达式进行了判断,不符合规则会报错
创建一个桶
makeBucket()
: 创建一个新的存储桶
@SpringBootTest
public class MinioTestDemo {
@Autowired
private MinioClient Client;
// makeBucket(): 创建一个的存储桶
@Test
public void testMakeBucket() throws Exception {
boolean exists = Client.bucketExists(BucketExistsArgs.builder().bucket("mybucket").build());
//如果mybucket不存在则创建
if(!exists){
Client.makeBucket(
MakeBucketArgs
.builder()
.bucket("mybucket")//参数为创建的桶的名字
.build()
);
}else{
System.out.println("名为mybucket的存储桶已经存在!");
}
}
}
执行前
执行后
返回桶列表
listBuckets()
:用于列出用户有权访问的所有存储桶,返回存储桶的列表
@SpringBootTest
public class MinioTestDemo {
@Autowired
private MinioClient Client;
// 列出所有的存储桶
@Test
public void testListBuckets() throws Exception {
List<Bucket> buckets = Client.listBuckets();
buckets.forEach(bucket -> System.out.println(bucket.name()));
}
}
打印结果:
mybucket
删除一个桶
removeBucket()
:用于删除一个已存在的桶,删除失败会抛出异常
@SpringBootTest
public class MinioTestDemo {
@Autowired
private MinioClient Client;
// 删除一个存储桶
@Test
public void testRemoveBucket() throws Exception {
//指定删除名为mybucket的桶
Client.removeBucket(RemoveBucketArgs.builder().bucket("mybucket").build());
List<Bucket> buckets = Client.listBuckets();
//因为目前只有一个桶mybucket,所以idea控制台无输出
buckets.forEach(bucket -> System.out.println(bucket.name()));
}
}
管理页面执行删除前:
执行后:
针对文件的操作
文件需要上传到桶中,文件在minio中即为Object,也就是对象
上传文件到桶中(本地文件上传)
uploadObject()
:直接选择上传文件到桶
// 向桶中上传文件
@Test
public void testPutObject() throws Exception {
ObjectWriteResponse mybucket = Client.uploadObject(UploadObjectArgs.builder()
.bucket("mybucket")
.object("test002.png")
.filename("C:\\home\\minio\\test001.png")
.build()
);
System.out.println(mybucket);
}
控制台输出结果:
io.minio.ObjectWriteResponse@1abebef3
查看管理界面:
Buckets中:
Object Browser中:
点击Object Browser中的mybucket:
上传文件到桶中(基于io流上传)
putObject()
@SpringBootTest
public class MinioTestDemo {
@Autowired
private MinioClient Client;
// 向桶中上传文件
@Test
public void testPutObject() throws Exception {
File file = new File("C:\\home\\minio\\test001.png");
Client.putObject(
PutObjectArgs.builder()
.bucket("mybucket") //将文件上传到哪个桶
.object("test001.png") //上传到桶后该文件的名字
.stream(new FileInputStream(file),file.length(),-1) //构建一个流用于上传文件
.build()
);
}
}
关于上面的
stream
方法
stream(InputStream stream, long objectSize, long partSize)
方法的主要目的是设置要上传的数据流,以及处理上传时的块大小和对象的总大小。这个方法主要用于大文件的分块上传,如在 AWS S3 或 Minio 中上传文件时使用的多部分上传模式。
参数解释:
-
InputStream stream
:要上传的输入流,通常是文件、网络流或其他形式的数据流。- 输入流可以是任意类型,但代码会使用
BufferedInputStream
包装它以提高效率。
- 输入流可以是任意类型,但代码会使用
-
long objectSize
:上传对象的总大小。- 如果对象大小未知(如无法提前知道文件大小),可以传递
-1
,表示未知大小。 - 如果知道对象的大小,可以指定具体大小,这有助于优化上传。
- 如果对象大小未知(如无法提前知道文件大小),可以传递
- 上传文件的最大大小不能超过
5TB
。
long partSize
:分块上传时,每一部分的大小。- 如果不希望手动指定分块大小,可以传递
-1
,自动根据对象大小选择合适的块大小。 - 如果你知道需要特定大小的块上传(为了控制内存使用或上传效率),可以指定该大小。
- 如果
partSize
大于objectSize
,那么直接将objectSize
作为partSize
。 - 合规的
partSize
大小应该设置在5M
到5G
之间
- 如果不希望手动指定分块大小,可以传递
源码:
/**
* Sets stream to upload. Two ways to provide object/part sizes.
*
* <ul>
* <li>If object size is unknown, pass -1 to objectSize and pass valid partSize.
* <li>If object size is known, pass -1 to partSize for auto detect; else pass valid partSize
* to control memory usage and no. of parts in upload.
* <li>If partSize is greater than objectSize, objectSize is used as partSize.
* </ul>
*
* <p>A valid part size is between 5MiB to 5GiB (both limits inclusive).
*/
public Builder stream(InputStream stream, long objectSize, long partSize) {
validateNotNull(stream, "stream");
long[] partinfo = getPartInfo(objectSize, partSize);
long pSize = partinfo[0];
int pCount = (int) partinfo[1];
final BufferedInputStream bis =
(stream instanceof BufferedInputStream)
? (BufferedInputStream) stream
: new BufferedInputStream(stream);
return setStream(bis, objectSize, pSize, pCount);
}
执行方法结果(执行成功后会由延迟,可稍后查看)
Buckets
页签中:
Object Browser
页签中,点击mybucket:
Controller上传文件到minio
Controller代码,使用基于流的方式上传,接收前端的文件并从后端传到minio中
在项目中添加如下Controller
import io.minio.MinioClient;
import io.minio.ObjectWriteResponse;
import io.minio.PutObjectArgs;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
@RequestMapping("upload")
@RestController
public class MinioController {
@Autowired
private MinioClient Client;
@PostMapping("test")
public String uploadFile(@RequestPart("file") MultipartFile file) {
ObjectWriteResponse mybucket = null;
//try-catch-resource方式,可以自动关闭流
try (InputStream inputStream = file.getInputStream()) {
mybucket = Client.putObject(
PutObjectArgs.builder()
.bucket("mybucket") //将文件上传到哪个桶
.object(file.getOriginalFilename()) //上传到桶后该文件的名字
.stream(inputStream, file.getSize(), -1) //构建一个流用于上传文件
.build()
);
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(mybucket);
return mybucket.toString();
}
}
postman测试
注意测试时的类型是表单,一般都是自动带出不需要改动
测试结果,查看minio管理页面
检查某文件的状态
statObject()
:检查某个桶中的某个文件的状态,也可以用来检测其是否存在
@SpringBootTest
public class MinioTestDemo {
@Autowired
private MinioClient Client;
@Test
public void testStatObject() throws Exception {
StatObjectResponse mybucket =
Client.statObject(StatObjectArgs.builder()
.bucket("mybucket")
.object("test002.png")
.build()
);
System.out.println(mybucket);
}
}
输出结果:
ObjectStat{bucket=mybucket, object=test002.png, last-modified=2024-09-19T08:16:55Z, size=716272}
如果文件不存在,会报错:
error occurred
ErrorResponse(code = NoSuchKey, message = Object does not exist
, bucketName = mybucket, objectName = test008.png, resource = /mybucket/test008.png, requestId = 17F69873D6323309, hostId = dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8)
request={method=HEAD, url=http://192.168.137.200:9000/mybucket/test008.png, headers=Host: 192.168.137.200:9000
Accept-Encoding: identity
User-Agent: MinIO (Windows 10; amd64) minio-java/8.5.12
Content-MD5: 1B2M2Y8AsgTpgAmY7PhCfg==
x-amz-content-sha256: e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
x-amz-date: 20240919T083850Z
Authorization: ██
}
response={code=404, headers=Accept-Ranges: bytes
Content-Length: 0
Server: MinIO
Strict-Transport-Security: max-age=31536000; includeSubDomains
Vary: Origin
Vary: Accept-Encoding
X-Amz-Id-2: dd9025bab4ad464b049177c95eb6ebf374d3b3fd1af9251148b658df7ac2e3e8
X-Amz-Request-Id: 17F69873D6323309
X-Content-Type-Options: nosniff
X-Minio-Error-Code: NoSuchKey
X-Minio-Error-Desc: "The specified key does not exist."
X-Ratelimit-Limit: 1632
X-Ratelimit-Remaining: 1632
X-Xss-Protection: 1; mode=block
Date: Thu, 19 Sep 2024 08:38:50 GMT
}
at io.minio.S3Base$1.onResponse(S3Base.java:775)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:750)
Process finished with exit code -1
生成文件访问的URL
getPresignedObjectUrl()
:用于生成一个对象(文件)的签名URL,以便于使用http地址来直接访问该文件
@SpringBootTest
public class MinioTestDemo {
@Autowired
private MinioClient Client;
// 返回某个桶中某个文件的http访问地址,地址中带有签名字符串
@Test
public void testPresignedObjectUrl() throws Exception {
String url = Client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.bucket("mybucket")
.object("test002.png")
//.expiry(3, TimeUnit.MINUTES) //指定有效期为3分钟,地址生成3分钟后即失效无法在访问文件
.method(Method.GET) //设置访问文件url的请求方式为get
.build()
);
System.out.println(url);
}
}
输出结果:
http://192.168.137.200:9000/mybucket/test002.png?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20240919%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20240919T085803Z&X-Amz-Expires=180&X-Amz-SignedHeaders=host&X-Amz-Signature=b3d2d9fa782739e698b46b8a8193a60a49ac8741da472c118061b735c9b887d1
访问签名地址
浏览器访问,可以直接预览该图片:
如果不加签名串,直接访问地址
http://192.168.137.200:9000/mybucket/test002.png
则会变为如下页面:
如果想要不带签名串访问,需要做其他配置
第一种,需要在管理界面修改桶的访问策略
首先点击桶
点击后进入如下界面,修改红框处的访问策略:
将访问策略由private
改为public
,然后点击set
保存:
再次使用
http://192.168.137.200:9000/mybucket/test002.png
进行访问:
改变访问策略为公有后可以不带签名字符串直接访问,但是此种做法相当于谁都可以访问桶中的文件,并不安全。
第二种,Api方式,使用
MinioClient
的setBucketPolicy()
方法
创建桶mybucket01,并为其指定文件访问策略:
@SpringBootTest
public class MinioTestDemo {
@Autowired
private MinioClient Client;
@Test
public void testBucketPolicy() throws Exception {
boolean exists = Client.bucketExists(BucketExistsArgs.builder().bucket("mybucket01").build());
if(!exists){
Client.makeBucket(
MakeBucketArgs
.builder()
.bucket("mybucket01")//参数为创建的桶的名字
.build()
);
}else{
System.out.println("名为mybucket01的存储桶已经存在!");
}
//策略字符串
String policyJson = "{\n" +
" \"Version\": \"2012-10-17\",\n" +
" \"Statement\": [\n" +
" {\n" +
" \"Effect\": \"Allow\",\n" +
" \"Principal\": {\n" +
" \"AWS\": [\"*\"]\n" +
" },\n" +
" \"Action\": [\"s3:GetObject\"],\n" +
" \"Resource\": [\"arn:aws:s3:::mybucket01/*\"]\n" +
" }\n" +
" ]\n" +
"}\n";
//为上面创建的桶设置文件访问策略,访问策略为public,即公开读取
Client.setBucketPolicy(
SetBucketPolicyArgs.builder()
.bucket("mybucket01")
.config(policyJson)
.build())
;
}
}
上面的json串解释:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"AWS": ["*"]
},
"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::mybucket01/*"]
}
]
}
Version
:指定策略的版本。Effect
:设为Allow
,表示允许操作。Principal
:"*"
表示允许任何用户(即公开访问)。Action
:s3:GetObject
允许对象的读取操作。Resource
:指定桶中所有对象的 ARN(arn:aws:s3:::mybucket01/*
),最后的冒号后面是桶的名字。
运行成功后访问管理页面:
进入新建的桶mybucket01中,发现策略为custom
自定义策略,虽然为自定义策略,但是根据config的参数,桶中文件是公开读的
执行上传代码进行测试:
// 向桶中上传文件
@Test
public void testPutObject() throws Exception {
ObjectWriteResponse mybucket = Client.uploadObject(UploadObjectArgs.builder()
.bucket("mybucket01")
.object("test001.png")
.filename("C:\\home\\minio\\test001.png")
.build()
);
System.out.println(mybucket);
}
直接通过ip:port/桶名/文件名来访问:
http://192.168.137.200:9000/mybucket01/test001.png
设置成功。
最后,还可以通过手写json串的方式来实现自己想要的任何文件访问策略
在json串的Statement
内编写策略,最后set保存即可:
从指定的桶中下载文件
getObject()
:用于从指定的存储桶中下载文件
@SpringBootTest
public class MinioTestDemo {
@Autowired
private MinioClient Client;
@Test
public void testGetObject() throws Exception {
GetObjectResponse mybucket01 = Client.getObject(GetObjectArgs.builder()
.bucket("mybucket01")
.object("test001.png")
.build());
// 文件保存路径
String filePath = "C:\\home\\minio\\test\\test001.png";
// 将文件内容从流中读取并写入文件
try (InputStream inputStream = mybucket01; // GetObjectResponse 是一个流,可以直接使用
OutputStream outputStream = new FileOutputStream(filePath)) {
byte[] buffer = new byte[8192]; // 每次读取8KB
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
执行结果
会将mybucket01桶中的test001.png下载到C:\home\minio\test文件夹中:
列出桶中所有文件
listObjects()
:用于列出指定存储桶中的所有对象(文件)
@SpringBootTest
public class MinioTestDemo {
@Autowired
private MinioClient Client;
@Test
public void testListObjects() {
//获取mybucket01桶中的所有对象(文件)
Iterable<Result<Item>> results = Client.listObjects(ListObjectsArgs.builder()
.bucket("mybucket01")
.build());
//循环输出文件名
results.forEach(r -> {
try {
Item item = r.get();
System.out.println(item.objectName());
} catch (Exception e) {
e.printStackTrace();
}
});
}
}
输出结果:
test001.png
删除指定桶中的文件
removeObject()
:用于删除指定存储桶中的对象,需要指定存储桶名称和对象键。如果抛出异常则代表删除失败
@SpringBootTest
public class MinioTestDemo {
@Autowired
private MinioClient Client;
// 删除mybucket01桶中的test001.png文件
@Test
public void testRemoveObject() throws Exception {
//无返回值方法,如果异常则删除失败
Client.removeObject(RemoveObjectArgs.builder().bucket("mybucket01").object("test001.png").build());
}
}
执行前
执行后
纠删码模式单机部署
纠删码介绍
纠删码
(Erasure Code)简称EC,是一种数据保护方法,也是一种算法;- MinlO对纠删码模式的算法进行了实现,采用Reed-Solomon code(简称RScode)纠错码将对象拆分成N/2数据块和N/2奇偶校验块,Reed Solomon利用范德蒙矩阵(Vandermonde matrix)、柯西矩阵(Cauchy matrix)的特性来实现;
- 即将数据拆分为多个数据块和多个校验块,分散存储在不同的磁盘上,即使在部分磁盘损坏或丢失的情况下,也可以通过剩余的数据块和校验块恢复出原始数据;
- 举个例子,现在有12块磁盘,一个对象数据会被分成6个数据块、6个奇偶校验块,你可以损坏或丢失任意6块磁盘(不管其是存放的数据块还是奇偶校验块),你仍可以从剩下的磁盘中恢复数据;
单机多磁盘部署准备
纠删码模式 单机多磁盘部署前需要添加磁盘,这里以虚拟机为例演示,按如下步骤执行:
-
执行命令:
lsblk
lsblk
是Linux中的一个命令,用于列出所有可用的块设备(数据存储设备,如硬盘、闪存驱动器)的信息,如设备名称、大小、挂载点等;[root@Centos7 minio]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 50G 0 disk ├─sda1 8:1 0 1G 0 part /boot ├─sda2 8:2 0 19G 0 part │ ├─centos-root 253:0 0 41G 0 lvm / │ └─centos-swap 253:1 0 2G 0 lvm [SWAP] └─sda3 8:3 0 30G 0 part └─centos-root 253:0 0 41G 0 lvm / sr0 11:0 1 4.4G 0 rom /run/media/root/CentOS 7 x86_64
sda即为现有磁盘,大小为50GB
-
添加一块磁盘:
关闭虚拟机,右键设置,添加磁盘:
选中硬盘下一步后选择硬盘类型为推荐的
SCSI
即可,再下一步选创建新虚拟磁盘
,继续下一步出现如下磁盘分配页面,分配自定义大小,然后下一步:出现如下页面点击完成即可:
最后虚拟机信息如下,然后重新开机:
重新开机后再次执行
lsblk
,发现多了一个1GB的sdb磁盘:[root@Centos7 ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 50G 0 disk ├─sda1 8:1 0 1G 0 part /boot ├─sda2 8:2 0 19G 0 part │ ├─centos-root 253:0 0 41G 0 lvm / │ └─centos-swap 253:1 0 2G 0 lvm [SWAP] └─sda3 8:3 0 30G 0 part └─centos-root 253:0 0 41G 0 lvm / sdb 8:16 0 1G 0 disk sr0 11:0 1 4.4G 0 rom /run/media/root/CentOS 7 x86_64
-
将添加的磁盘格式化为xfs格式,以进行挂载: 执行命令
mkfs.xfs /dev/sdb
[root@Centos7 ~]# mkfs.xfs /dev/sdb meta-data=/dev/sdb isize=512 agcount=4, agsize=65536 blks = sectsz=512 attr=2, projid32bit=1 = crc=1 finobt=0, sparse=0 data = bsize=4096 blocks=262144, imaxpct=25 = sunit=0 swidth=0 blks naming =version 2 bsize=4096 ascii-ci=0 ftype=1 log =internal log bsize=4096 blocks=2560, version=2 = sectsz=512 sunit=0 blks, lazy-count=1 realtime =none
-
将磁盘挂载到minio的存储目录: 执行命令
mount /dev/sdb /opt/minio/data
,然后再lsblk
查看:如果提示挂载点 /opt/minio/data 不存在,则需要在根目录opt文件夹中去建立minio/data
[root@Centos7 ~]# mount /dev/sdb /opt/minio/data [root@Centos7 ~]# lsblk NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT sda 8:0 0 50G 0 disk ├─sda1 8:1 0 1G 0 part /boot ├─sda2 8:2 0 19G 0 part │ ├─centos-root 253:0 0 41G 0 lvm / │ └─centos-swap 253:1 0 2G 0 lvm [SWAP] └─sda3 8:3 0 30G 0 part └─centos-root 253:0 0 41G 0 lvm / sdb 8:16 0 1G 0 disk /opt/minio/data sr0 11:0 1 4.4G 0 rom /run/media/root/CentOS 7 x86_64
sdb后出现 /opt/minio/data 挂载点
纠删码模式启动
使用纠删码模式进行后台启动,命令:
MINIO_ROOT_USER=admin MINIO_ROOT_PASSWORD=password nohup ./minio server --console-address ":9001" --address ":9000" /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 会话也不会被阻塞,你可以继续执行其他命令;
进入minio所在目录,执行命令
[root@Centos7 ~]# cd /home/minio
[root@Centos7 minio]# MINIO_ROOT_USER=admin MINIO_ROOT_PASSWORD=password nohup ./minio server --console-address ":9001" --address ":9000" /opt/minio/data/data{1...12} > /opt/minio/data/minio.log 2>&1 &
[1] 6958
[root@Centos7 minio]# ps -ef|grep minio
root 6958 3011 8 14:41 pts/0 00:00:00 ./minio server --console-address :9001 --address :9000 /opt/minio/data/data{1...12}
root 6985 3011 0 14:41 pts/0 00:00:00 grep --color=auto minio
访问管理页面
虚拟机ip加9001端口访问:
进入/opt/minio/data
目录查看,多出了12个minio存储的目录以及一个日志文件
[root@Centos7 minio]# cd /opt/minio/data
[root@Centos7 data]# ll
总用量 4
drwxr-xr-x. 3 root root 24 9月 21 14:41 data1
drwxr-xr-x. 3 root root 24 9月 21 14:41 data10
drwxr-xr-x. 3 root root 24 9月 21 14:41 data11
drwxr-xr-x. 3 root root 24 9月 21 14:41 data12
drwxr-xr-x. 3 root root 24 9月 21 14:41 data2
drwxr-xr-x. 3 root root 24 9月 21 14:41 data3
drwxr-xr-x. 3 root root 24 9月 21 14:41 data4
drwxr-xr-x. 3 root root 24 9月 21 14:41 data5
drwxr-xr-x. 3 root root 24 9月 21 14:41 data6
drwxr-xr-x. 3 root root 24 9月 21 14:41 data7
drwxr-xr-x. 3 root root 24 9月 21 14:41 data8
drwxr-xr-x. 3 root root 24 9月 21 14:41 data9
-rw-r--r--. 1 root root 789 9月 21 14:41 minio.log
此时,12个存储目录中都是空的,从管理控制台上传文件:
Buckets页签下创建桶:
输入桶名字后点击创建,注意名字要符合规则:
创建完成后如下:
在Object Browser页签下点击上传文件:
上传一个图片,如下:
完成后可以发现每一个存储目录下都会出现此图片的元数据文件:
至此完成。