1、基本概念
1.1、MinIO介绍
MinIO 是在 Apache License v2.0 下发布的对象存储服务器
MinIO 服务器足够轻,可以与应用程序堆栈捆绑在一起,类似于 NodeJS,Redis 和 MySQL
MinIO 是一种高性能的分布式对象存储服务器,用于大型数据基础设施
1.2、基础概念
Object:存储到MinIO的基本对象,就是每个上传的文件
Bucket:用来存储Object的逻辑空间,每个Bucket之间的数据是相互隔离的。对于客户端而言,相当于一个存放文件的顶层文件夹
Drive:存储数据的磁盘,在MinIO启动时,以参数的方式传入。MinIO中所有的对象数据都存储在Drive里
Set:一组Drive的集合,分布式部署根据集群规模自动划分一个或多个Set,每个Set中的Drive分布在不同位置
一个对象存储在一个Set上
一个集群划分为多个Set
一个Set包含的Drive数量是固定的,默认由系统根据集群规模自动计算得出
一个Set中的Drive尽可能分布在不同节点上
1.3、MinIO优点
部署简单,支持各种平台
minio支持海量存储,支持单个对象最大5TB
低冗余且磁盘损坏高容忍,标准且最高的数据冗余系数为2(即存储一个1M的数据对象,实际占用磁盘空间为2M)。但在任意n/2块disk损坏的情况下依然可以读出数据(n为一个纠删码集合(Erasure Coding Set)中的disk数量)。并且这种损坏恢复是基于单个对象的,而不是基于整个存储卷的
读写性能好
1.4、纠删码EC
Erasure Code,MinlO使用纠删码机制来保证高可靠性,使用highwayhash来处理数据损坏
纠删码是一种恢复丢失和损坏数据的数学算法,MinIO采用Reed-Solomon code将对象拆分为N/2奇偶校验块。比如若是12块盘,一个对象会被分为6个数据块和6个奇偶校验块,可以任意丢失6块盘,仍可以从剩下的盘中的数据进行恢复
纠删码可通过数学计算,把丢失的数据进行还原,它可以将n份原始数据,增加m份数据,并能通过n+m份中的任意n份数据,还原为原始数据
若有任意小于等于m份的数据失效,仍然能通过剩下的数据还原出来
1.5、存储形式
文件上传到MinIO,会在对应的数据磁盘中,以Bucket名为目录,文件名为下一级目录,文件名下是part.1(编码数据块及检验块)和xl.meta(元数据文件)
2、常用API
2.1、判断桶是否存在
boolean bucketExists()
minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
2.2、创建桶
void makeBucket()
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
2.3、显示所有桶信息
List listBuckets()
minioClient.listBuckets();
2.4、列出桶的对象信息
Iterable listObjects()
minioClient.listObjects(ListObjectsArgs.builder()
.bucket(bucketName)
.startAfter("2022")
.prefix("2") // 指定前缀
.maxKeys(100) // 最大数量
.recursive(true) //递归显示桶下的对象
.build()
);
2.5、删除桶
void removeBucket()
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
2.6、通过流上传文件
ObjectWriteResponse putObject()
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build()
);
添加的Object大小不能超过 5GB
若添加的Object和已有的同名,会进行覆盖
OSS没有文件夹的概念,所有资源都是以文件来存储,可通过“/”模拟文件夹
2.7、上传文件内容
void uploadObject()(不常用,一般适合上传磁盘文件)
minioClient.uploadObject(UploadObjectArgs
.builder()
.bucket(bucketName)
.object(fileName) //上传到minio的对象名
.filename(fileName) //要上传的文件
.build()
);
2.8、获取对象数据
GetObjectResponse getObject()
minioClient.getObject(GetObjectArgs
.builder()
.bucket(bucketName)
.object(fileName)
.build()
);
2.9、下载文件
void downloadObject()
minioClient.downloadObject(DownloadObjectArgs
.builder()
.bucket(bucketName)
.object(fileName) //minio中的对象名(目录用"/"隔开)
.filename("D:\\test.txt") //下载到磁盘的路径
.build()
);
2.10、删除文件
void removeObject()
minioClient.removeObject(
RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
2.11、复制文件
ObjectWriteResponse copyObject()
minioClient.copyObject(CopyObjectArgs
.builder()
.source(CopySource.builder()
.bucket(bucketName1)
.object(fileName1)
.build()
)
.bucket(bucketName2)
.object(fileName2)
.build()
);
在 bucketName2 中创建一个和 bucketName1 中的 fileName1 一样的对象 fileName2
3、整合SpringBoot(单机版)
3.1、配置文件
minio:
endpoint: http://127.0.0.1:9000
accessKey: minioadmin
secretKey: minioadmin
bucketName: bucket-1
3.2、配置类
@Configuration
publicclassMinioConfig {
@Value("${minio.endpoint}")
privateStringendpoint;
@Value("${minio.accessKey}")
privateStringaccessKey;
@Value("${minio.secretKey}")
privateStringsecretKey;
@Bean
publicMinioClientminioClient(){
returnMinioClient
.builder()
.endpoint(endpoint)
.credentials(accessKey,secretKey)
.build();
}
}
3.3、Controller层
@Autowired
privateMinioClientminioClient;
@Value("${minio.bucketName}")
privateStringbucketName;
3.3.1、显示所有桶
@GetMapping("/list")
publicList<Object>list() throwsException {
//获取bucket列表
Iterable<Result<Item>>buckets=minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).build()
);
Iterator<Result<Item>>iterator=buckets.iterator();
List<Object>list=newArrayList<>();
while (iterator.hasNext()) {
Itemitem=iterator.next().get();//文件对象
list.add(JSON.parse(String.format(
"{'fileName':'%s','fileSize':'%s'}",
item.objectName(),//文件名
formatFileSize(item.size()))));//文件大小
}
returnlist;
}
//格式化文件大小
privatestaticStringformatFileSize(longfileSize) {
DecimalFormatdf=newDecimalFormat("#.00");
if (fileSize==0) {
return"0B";
}
StringfileSizeString;
if (fileSize<1024) {
fileSizeString=df.format((double) fileSize) +"B";
} elseif (fileSize<1048576) {
fileSizeString=df.format((double) fileSize/1024) +"KB";
} elseif (fileSize<1073741824) {
fileSizeString=df.format((double) fileSize/1048576) +"MB";
} else {
fileSizeString=df.format((double) fileSize/1073741824) +"GB";
}
returnfileSizeString;
}
3.3.2、文件上传
@PostMapping("/upload")
publicStringupload(
@RequestParam(name="file", required=false) MultipartFilefile) {
if (file==null) {
return"上传失败,文件为空";
}
StringfileName=file.getOriginalFilename();
try {
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build()
);
} catch (Exceptione) {
e.printStackTrace();
}
return"bucketName:"+bucketName+",fileName:"+fileName;
}
3.3.3、文件下载
@GetMapping("/download/{fileName}")
publicStringdownload(HttpServletResponseres, @PathVariable("fileName") StringfileName) {
try {
//获取对象信息
StatObjectResponsestatObject=minioClient.statObject(
StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
res.setContentType(statObject.contentType());
res.setHeader(
"Content-Disposition",
"attachment;fileName"+URLEncoder.encode(fileName, "utf-8")
);
//下载
minioClient.downloadObject(DownloadObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.filename(fileName)
.build()
);
return"下载成功";
} catch (Exceptione) {
e.printStackTrace();
}
return"下载失败";
}
3.3.4、文件删除
@DeleteMapping("/delete/{fileName}")
publicStringdelete(@PathVariable("fileName") StringfileName) {
try {
minioClient.removeObject(
RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
return"删除成功";
} catch (Exceptione) {
e.printStackTrace();
}
return"删除失败";
}
3.3.5、文件批量上传
@PostMapping("/uploadBatch")
publicStringuploadBatch(
@RequestParam(name="file", required=false) MultipartFile[] files) {
if (files==null||files.length==0) {
return"上传失败,文件为空";
}
List<String>fileNames=newArrayList<>(files.length);
for (MultipartFilefile : files) {
StringoriginalFilename=file.getOriginalFilename();
fileNames.add(originalFilename);
try {
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(originalFilename)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build()
);
} catch (Exceptione) {
e.printStackTrace();
}
}
return"bucketName:"+bucketName+",fileNames:"+fileNames;
}