阅读完需:约 21 分钟
MinIO 介绍
Minio 是个基于 Golang 编写的开源对象存储套件,虽然轻量,却拥有着不错的性能。
官方网站
https://min.io/product/enterpriseoverview
https://github.com/minio/minio
对象存储服务(Object Storage Service,OSS)是一种海量、安全、低成本、高可靠的云存储服务,适合存放任意类型的文件。容量和处理能力弹性扩展,多种存储类型供选择,全面优化存储成本。
MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。
它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。
对于中小型企业,如果不选择存储上云,那么 Minio 是个不错的选择,麻雀虽小,五脏俱全。
当然 Minio 除了直接作为对象存储使用,还可以作为云上对象存储服务的网关层,无缝对接到 Amazon S3、MicroSoft Azure。
MINIO 基础概念
MINIO 有几个概念比较重要:
- Object:存储到 Minio 的基本对象,如文件、字节流,Anything…
- Bucket:用来存储 Object 的逻辑空间。每个 Bucket 之间的数据是相互隔离的。对于客户端而言,就相当于一个存放文件的顶层文件夹。
- Drive:即存储数据的磁盘,在 MinIO 启动时,以参数的方式传入。Minio 中所有的对象数据都会存储在 Drive 里。
- Set:即一组 Drive 的集合,分布式部署根据集群规模自动划分一个或多个 Set ,每个 Set 中的 Drive 分布在不同位置。一个对象存储在一个 Set 上。(For example: {1…64} is divided into 4 sets each of size 16.)
- 一个对象存储在一个Set上
- 一个集群划分为多个Set
- 一个Set包含的Drive数量是固定的,默认由系统根据集群规模自动计算得出
- 一个SET中的Drive尽可能分布在不同的节点上
Set / Drive 的关系
Set /Drive 这两个概念是 MINIO 里面最重要的两个概念,一个对象最终是存储在 Set 上面的。
我们来看下边 MINIO 集群存储示意图,每一行是一个节点机器,这有 32 个节点,每个节点里有一个小方块我们称之 Drive,Drive 可以简单地理解为一个硬盘。
图中,一个节点有 32 个 Drive,相当于 32 块硬盘。
Set 是另外一个概念,Set 是一组 Drive 的集合,图中,所有蓝色、橙色背景的Drive(硬盘)的就组成了一个 Set.

MIINO如何写入对象?
MINIO 是通过数据编码,将原来的数据编码成 N 份,N 就是一个 Set 上面 Drive 的数量,后面多次提到的 N 都是指这个意思。
上图中,一个 Set 上面 Drive 的数量,是3.
对象被编码成N份之后,把每一份,写到对应的 Drive 上面,这就是把一个对象存储在整个 Set 上。

一个集群包含多个 Set,每个对象最终存储在哪个 Set 上是根据对象的名称进行哈希,然后影射到唯一的 Set 上面,这个方式从理论上保证数据可以均匀的分布到所有的 Set 上。
根据的观测,数据分布的也非常均匀,一个 Set 上包含多少个 Drive 是由系统自动根据集群规模算出来的,当然,也可以自己去配置。
一个 Set 的 Drive 系统会考虑尽可能把它放在多的节点上面,保证它的可靠性。
Minio存储架构
Minio针对不同应用场景也设置了对应的存储架构:
单主机,单硬盘模式

该模式下,Minio只在一台服务器上搭建服务,且数据都存在单块磁盘上,该模式存在单点风险,主要用作开发、测试等使用
单主机,多硬盘模式

该模式下,Minio在一台服务器上搭建服务,但数据分散在多块(大于4块)磁盘上,提供了数据上的安全保障
多主机、多硬盘模式(分布式)

该模式是Minio服务最常用的架构,通过共享一个access_key和secret_key,在多台(2-32)服务器上搭建服务,且数据分散在多块(大于4块,无上限)磁盘上,提供了较为强大的数据冗余机制(Reed-Solomon纠删码)。
分布式Minio有什么好处?
在大数据领域,通常的设计理念都是无中心和分布式。Minio分布式模式可以帮助你搭建一个高可用的对象存储服务,你可以使用这些存储设备,而不用考虑其真实物理位置。
数据保护
分布式Minio采用 纠删码来防范多个节点宕机和位衰减bit rot。
分布式Minio至少需要4个硬盘,使用分布式Minio自动引入了纠删码功能。
高可用
单机Minio服务存在单点故障,相反,如果是一个有N块硬盘的分布式Minio, 只要有N/2硬盘在线,你的数据就是安全的。
不过你需要至少有N/2+1个硬盘来创建新的对象。
例如,一个16节点的Minio集群,每个节点16块硬盘,就算8台服务器宕机,这个集群仍然是可读的,不过你需要9台服务器才能写数据。
注意,只要遵守分布式Minio的限制,你可以组合不同的节点和每个节点几块硬盘。
比如,你可以使用2个节点,每个节点4块硬盘,也可以使用4个节点,每个节点两块硬盘,诸如此类。
一致性
Minio在分布式和单机模式下,所有读写操作都严格遵守read-after-write一致性模型。
MinIO的数据高可靠
Minio使用了Erasure Code
纠删码和 Bit Rot Protection
数据腐化保护这两个特性,所以MinIO的数据可靠性做的高。
Erasure Code
纠删码
纠删码(Erasure Code)简称EC,是一种数据保护方法,它将数据分割成片段,把冗余数据块扩展、编码,并将其存储在不同的位置,比如磁盘、存储节点或者其它地理位置。
从数据函数角度来说,纠删码提供的保护可以用下面这个简单的公式来表示:n = k + m。变量“k”代表原始数据或符号的值。变量“m”代表故障后添加的提供保护的额外或冗余符号的值。变量“n”代表纠删码过程后创建的符号的总值。
举个例子,假设n=16,代表有16块磁盘,另外,有10份原始文件一模一样,称为k,16 = 10 +m,这个m就是可以恢复的校验块个数,所以m是6,任意6个不可用,原始文件都可以恢复,极端情况,10个原始文件坏掉6个,靠4个原始的加上6个校验块,可以把坏掉的6个原始文件恢复。
MinIO的编码方式,将一个对象编码成若干个数据块和校验块,我们简称为Erasure Code码,这个是编码的类型,这种编码的类型,还需要算法来实现,minio 采用的是 Reed-Solomon算法。
MinIO使用Reed-Solomon算法,该算法把对象编码成若干个数据块和校验块。
Reed-Solomon算法的特点:
- 低冗余
- 高可靠
为了表述方便,把数据块和校验块统称为编码块,之后我们可以通过编码块的一部分就能还原出整个对象。
Reed-Solomon code
Reed-Solomon 是纠删码的实现算法的一种,当然,也是一种恢复丢失和损坏数据的数学算法,
Minio默认采用Reed-Solomon code将数据拆分成N/2个数据块和N/2个奇偶校验块。
这就意味着如果是16块盘,一个对象会被分成8个数据块、8个奇偶校验块,你可以丢失任意8块盘(不管其是存放的数据块还是校验块),你仍可以从剩下的盘中的数据进行恢复。

如上图,如我们所知,一个对象存储在一个Set上面,这个Set包含16个Drive,其中灰色的一半是数据库,橙色的一半是校验块,这种方式最多能忍受一半的编码丢失或损坏。
所有编码块的大小是原对象的2倍,跟传统多副本存储方案相比,他只冗余存了一份,但可靠性更高。
纠删码的工作原理和RAID或者副本不同,像RAID6只能在损失两块盘,或者以下的情况下不丢数据,而Minio纠删码可以在丢失一半的盘的情况下,仍可以保证数据安全。
而且Minio纠删码是作用在对象级别,可以一次恢复一个对象,而RAID是作用在卷级别,数据恢复时间很长。
Minio对每个对象单独编码,存储服务一经部署,通常情况下是不需要更换硬盘或者修复。
此外,针对不同应用所需的数据安全级别不同,Minio还提供了存储级别(Storage Class)的配置,调整数据块和校验块的比例,做到对空间的最佳使用。

比如在将比例调整为14:2后,存储100M的数据占用的空间仅为114M。
Bit Rot Protection
接下来讲Bit Rot Protection,直译就是腐烂。
它只是物理设备上的一些文件细微的损坏,还没有被操作系统所硬件所察觉,但是他已经损坏了。
Bit Rot 位衰减又被称为数据腐化Data Rot、无声数据损坏Silent Data Corruption
位衰减可以理解为无形中的数据丢失——或者称为“Bit rot”, 是指物理存储介质的衰减所带来的隐患将凸显出来。
位衰减是目前硬盘数据的一种严重数据丢失问题。
硬盘上的数据可能会神不知鬼不觉就损坏了,也没有什么错误日志。
一项对150万块硬盘的研究表明,每90块硬盘就有1块有这种“软错误”,这个错误不但会导致数据丢失,还会导致RAID错误。
针对这一问题,最新的Minio采用了HighwayHash算法计算校验和来防范位衰减,根据测试结果,其可以实现10GB/s的处理速度。
大致的做法是:
MinIO把之前的编码块进行 HighwayHash 编码,最后要校验这个编码,以确保每个编码是正确的。
文件的修复 :
另外,MinIO提供了一个管理工具,可以对所有编码块进行校验,如果发现编码块有问题,再去修复它。
得益于Reed-Solomon纠删码,Minio可以更加灵活的对文件进行修复。
部署实操
部署的方式有很多,K8S,docker,单机,分布式等等
https://min.io/docs/minio/container/index.html
https://hub.docker.com/r/minio/minio
这里测试开发用的是docker的方式部署,单机部署
docker run -d -p 9000:9000 -p 9001:9001 quay.io/minio/minio server /data --console-address ":9001"
页面的端口是9001,服务的端口是9000
默认的账号密码都是 minioadmin
,部署后就可以打开页面查看

Springboot集成MinIO基础实现
Java 版 MinIO 客户端 SDK
https://github.com/minio/minio-java
https://min.io/docs/minio/linux/developers/java/API.html
Maven 依赖配置
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.10</version>
</dependency>
Minio yml配置
minio:
endpoint: http://82.157.xxx.xx:9000
bucketName: test001
accessKey: XfBJZbrB8szv1GrH3NFH
secretKey: OdloBZdI1QezuFEYyqr81kd3JbymKI03BsQLyNqj
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;
/**
* Minio配置
*
* @author e
* @date 2024/06/11
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {
private String endpoint;
private String accessKey;
private String secretKey;
private String bucketName;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
}
}
Minio 对象创建接收
import lombok.Builder;
import lombok.Data;
/**
* 对象项
*
* @author e
* @date 2024/06/13
*/
@Data
@Builder
public class ObjectItem {
private String objectName;
private Boolean directory;
private String lastModified;
private String size;
private String url;
}
Minio 核心工具
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
/**
* IO工具
*
* @author e
* @date 2024/06/12
*/
@Component
@Slf4j
public class MinioUtils {
@Resource
private MinioConfig minioConfig;
@Resource
private MinioClient minioClient;
/**
* 判断桶是否存在
*
* @param bucketName 桶名称
* @return {@link Boolean}
*/
public Boolean existBucket(String bucketName) {
boolean exists = false;
try {
exists = minioClient.bucketExists(
BucketExistsArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
log.error("判断桶是否存在异常", e);
}
return exists;
}
/**
* 创建存储桶
*
* @param bucketName 桶名称
* @return {@link Boolean}
*/
public Boolean makeBucket(String bucketName) {
if (Boolean.FALSE.equals(existBucket(bucketName))) {
try {
minioClient.makeBucket(
MakeBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
log.error("创建存储桶异常", e);
return false;
}
return true;
} else {
return false;
}
}
/**
* 删除存储桶
*
* @param bucketName 桶名称
* @return {@link Boolean}
*/
public Boolean removeBucket(String bucketName) {
if (Boolean.TRUE.equals(existBucket(bucketName))) {
try {
minioClient.removeBucket(
RemoveBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
log.error("删除存储桶异常", e);
return false;
}
return true;
}
return false;
}
/**
* 列出一个桶中的所有文件目录和大小信息
*
* @param bucketName 桶名称
* @return {@link List}<{@link ObjectItem}>
*/
public List<ObjectItem> getListFiles(String bucketName) {
if (Boolean.TRUE.equals(existBucket(bucketName))) {
Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
.bucket(bucketName)
.recursive(true)
.build());
List<ObjectItem> objectItems = new ArrayList<>();
results.forEach(result -> {
try {
Item item = result.get();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String fileSize = getFileSize(item.size());
String url = minioConfig.getEndpoint() + "/" + bucketName + "/" + item.objectName();
ObjectItem objectItem = ObjectItem.builder()
.objectName(item.objectName())
.directory(item.isDir())
.lastModified(item.lastModified()
.format(dateTimeFormatter))
.size(fileSize)
.url(url)
.build();
objectItems.add(objectItem);
} catch (Exception e) {
log.error("获取对象信息失败", e);
}
});
return objectItems;
}
return null;
}
/**
* 获取文件夹下所有对象
*
* @param bucketName 桶名称
* @param groupName 组名称 桶内开始于指定前缀
* @return {@link List}<{@link ObjectItem}>
*/
public List<ObjectItem> getObjectsByDir(String bucketName, String groupName) {
if (Boolean.TRUE.equals(existBucket(bucketName))) {
Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder()
.prefix(groupName)
.bucket(bucketName)
.recursive(true)
.build());
List<ObjectItem> objectItems = new ArrayList<>();
results.forEach(result -> {
try {
Item item = result.get();
DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String fileSize = getFileSize(item.size());
String url = minioConfig.getEndpoint() + "/" + bucketName + "/" + item.objectName();
ObjectItem objectItem = ObjectItem.builder()
.objectName(item.objectName())
.directory(item.isDir())
.lastModified(item.lastModified()
.format(dateTimeFormatter))
.size(fileSize)
.url(url)
.build();
objectItems.add(objectItem);
} catch (Exception e) {
log.error("获取对象信息失败", e);
}
});
return objectItems;
}
return null;
}
/**
* 迁移文件夹下所有对象到另一文件夹中
*
* @param bucketName 桶名称
* @param groupName 组名称
* @param newGroupName 新组名
*/
public void copyObjectByDir(String bucketName,String newBucketName, String groupName, String newGroupName) {
if (Boolean.TRUE.equals(existBucket(bucketName)) && Boolean.TRUE.equals(existBucket(newBucketName))) {
List<ObjectItem> objects = getObjectsByDir(bucketName, groupName);
objects.forEach((object -> {
String name = object.getObjectName();
String originName = name.substring(name.lastIndexOf('/') + 1);
String oldObjectName = null!= groupName ? groupName + "/" + originName : originName;
String newObjectName = null!= newGroupName ? newGroupName + "/" + originName : originName;
try {
//复制文件夹下所有对象到另一文件夹中
minioClient.copyObject(
CopyObjectArgs.builder()
.source(CopySource.builder().bucket(bucketName).object(oldObjectName).build())
.bucket(newBucketName)
.object(newObjectName)
.build()
);
//删除原有对象
minioClient.removeObject(RemoveObjectArgs.builder()
.bucket(bucketName)
.object(oldObjectName)
.build());
} catch (Exception e) {
log.error("复制文件夹下所有对象到另一文件夹中失败", e);
}
}));
}
}
/**
* 上传文件
*
* @param file 文件
* @param bucketName
*/
public String upload(MultipartFile file, String bucketName) {
try {
//给文件生成一个唯一名称 当日日期-uuid.后缀名
String folderName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd-HH-mm-ss"));
String fileName = String.valueOf(UUID.randomUUID());
//文件后缀名
String extName = Objects.requireNonNull(file.getOriginalFilename()).substring(file.getOriginalFilename().lastIndexOf("."));
String objectName = folderName + "-" + fileName + extName;
InputStream inputStream = file.getInputStream();
minioClient.putObject(PutObjectArgs
.builder()
.bucket(null != bucketName ? bucketName : minioConfig.getBucketName())
.object(objectName)
.stream(inputStream, file.getSize(), -1)
.contentType(file.getContentType())
.build());
log.info("文件上传成功,文件名为:{}", objectName);
return objectName;
} catch (Exception e) {
log.error("上传文件失败", e);
}
return null;
}
/**
* 下载文件
*
* @param fileName
* @param bucketName
* @return
*/
public ResponseEntity<byte[]> download(String fileName, String bucketName) {
ResponseEntity<byte[]> responseEntity = null;
InputStream in = null;
ByteArrayOutputStream out = null;
try {
//获取对象输入流
in = minioClient.getObject(GetObjectArgs.builder()
.bucket(null!= bucketName ?bucketName: minioConfig.getBucketName())
.object(fileName)
.build());
//创建输出流
out = new ByteArrayOutputStream();
//复制流
IOUtils.copy(in, out);
//封装返回值
byte[] bytes = out.toByteArray();
HttpHeaders headers = new HttpHeaders();
headers.setContentLength(bytes.length);
headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
headers.setAccessControlExposeHeaders(List.of("*"));
responseEntity = new ResponseEntity<>(bytes, headers, HttpStatus.OK);
} catch (Exception e) {
log.error("下载文件失败", e);
} finally {
IOUtils.closeQuietly(in);
IOUtils.closeQuietly(out);
}
return responseEntity;
}
/**
* 获取文件预览url
*
* @param fileName 文件名文件名
* @param bucketName 桶名称
* @return {@link String}
*/
public String getPresignedUrl(String fileName,String bucketName) {
String presignedUrl = null;
try {
// 判断桶是否存在
boolean isExist = existBucket(bucketName);
// 桶存在
if (isExist) {
presignedUrl = minioClient.getPresignedObjectUrl(
GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName)
.object(fileName)
.build());
return presignedUrl;
} else { // 桶不存在
log.error("获取文件预览url失败,桶{}不存在", bucketName);
}
}catch (Exception e){
log.error("获取文件预览url失败", e);
}
return presignedUrl;
}
/**
* 删除一个文件夹下所有文件
*
* @param groupName 组名称
* @param bucketName 桶名称
*/
public void removeObjectsByDir(String bucketName, String groupName) {
if (Boolean.TRUE.equals(existBucket(bucketName))) {
try {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder()
.bucket(bucketName)
.prefix(groupName)
.recursive(true)
.build()
);
for (Result<Item> itemResult : results) {
Item item = itemResult.get();
String objectName = item.objectName();
log.info("对象名称:" + objectName);
minioClient.removeObject(RemoveObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
}
} catch (Exception e) {
log.error("删除文件异常", e);
}
}
}
/**
* 删除对象(支持批量删除)
*
* @param bucketName 桶名称
* @param objects 对象
* @return {@link Boolean}
*/
public Boolean removeObjects(String bucketName, List<String> objects) {
if (Boolean.TRUE.equals(existBucket(bucketName))) {
List<DeleteObject> deleteObjects = objects.stream()
.map(DeleteObject::new).collect(Collectors.toList());
Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder()
.bucket(bucketName)
.objects(deleteObjects)
.build());
try {
for (Result<DeleteError> result : results) {
DeleteError error = result.get();
log.error("MinIo删除错误=>bucketName{},message=>{}", error.bucketName(), error.message());
}
} catch (Exception e) {
log.error("MinIo删除错误异常");
}
return true;
}
return false;
}
/**
* 判断对象大小
*
* @param size 大小
* @return {@link String}
*/
public static String getFileSize(long size) {
//如果字节数少于1024,则直接以B为单位,否则先除于1024,后3位因太少无意义
if (size < 1024) {
return size + "B";
} else {
size = size / 1024;
}
//如果原字节数除于1024之后,少于1024,则可以直接以KB作为单位
//因为还没有到达要使用另一个单位的时候
//接下去以此类推
if (size < 1024) {
return Math.round(size * 100) / 100 + "KB";
} else {
size = size / 1024;
}
if (size < 1024) {
//因为如果以MB为单位的话,要保留最后1位小数,
//因此,把此数乘以100之后再取余
return Math.round(size * 100) / 100 + "MB";
} else {
//否则如果要以GB为单位的,先除于1024再作同样的处理
return Math.round(size / 1024 * 100) / 100 + "GB";
}
}
}
测试接口
import com.linktopa.corex.multipart.core.MultipartData;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
/**
* 文件控制器
*
* @author e
* @date 2024/06/13
*/
@Slf4j
@RestController
@RequestMapping(value = "product/file")
public class FileController {
@Resource
private MinioUtils minioUtils;
/**
* 查看存储bucket是否存在
*
* @param bucketName 桶名称
* @return {@link MultipartData}
*/
@GetMapping("/bucketExists")
public Boolean bucketExists(@RequestParam("bucketName") String bucketName) {
return minioUtils.existBucket(bucketName);
}
/**
* 创建存储bucket
*
* @param bucketName 桶名称
* @return {@link MultipartData}
*/
@GetMapping("/makeBucket")
public Boolean makeBucket(String bucketName) {
return minioUtils.makeBucket(bucketName);
}
/**
* 删除存储bucket
*
* @param bucketName 桶名称
* @return {@link MultipartData}
*/
@DeleteMapping("/removeBucket")
public Boolean removeBucket(String bucketName) {
return minioUtils.removeBucket(bucketName);
}
/**
* 获取文件夹下所有对象
*
* @return {@link MultipartData}
*/
@GetMapping("/getObjectsByDir")
public List<ObjectItem> getObjectsByDir(String bucketName, String groupName) {
return minioUtils.getObjectsByDir(bucketName,groupName);
}
/**
* 列出一个桶中的所有文件目录和大小信息
*
* @param bucketName 桶名称
* @return {@link List}<{@link ObjectItem}>
*/
@GetMapping("/getListFiles")
public List<ObjectItem> getListFiles(String bucketName) {
return minioUtils.getListFiles(bucketName);
}
/**
* 迁移文件夹下所有对象到另一文件夹中
*
* @param bucketName 桶名称
* @return {@link List}<{@link ObjectItem}>
*/
@GetMapping("/copyObjectByDir")
public void copyObjectByDir(String bucketName, String newBucketName,String groupName, String newGroupName) {
minioUtils.copyObjectByDir(bucketName,newBucketName,groupName,newGroupName);
}
/**
* 文件上传返回名称
*
* @param file 文件
* @return {@link MultipartData}
*/
@PostMapping("/upload")
public String upload(@RequestParam("file") MultipartFile file,String bucketName) {
return minioUtils.upload(file,bucketName);
}
/**
* 文件下载
*
* @param fileName 文件名文件名
* @return {@link ResponseEntity}<{@link byte[]}>
*/
@GetMapping("/download")
public ResponseEntity<byte[]> download(@RequestParam("fileName") String fileName,String bucketName) {
return minioUtils.download(fileName,bucketName);
}
/**
* 获取文件预览url
*
* @param fileName 文件名文件名
* @param bucketName 桶名称
* @return {@link String}
*/
@GetMapping("/getPresignedUrl")
public String getPresignedUrl(String fileName,String bucketName){
return minioUtils.getPresignedUrl(fileName,bucketName);
}
/**
* 删除一个文件夹下所有文件
*
* @param groupName 组名称
* @param bucketName 桶名称
*/
@DeleteMapping("/removeObjectsByDir")
public void removeObjectsByDir(String bucketName, String groupName) {
minioUtils.removeObjectsByDir(bucketName,groupName);
}
/**
* 删除一个文件夹下所有文件
*
* @param bucketName 桶名称
* @param objects 对象
* @return {@link Boolean}
*/
@DeleteMapping("/removeObjects")
public Boolean removeObjects(String bucketName, @RequestParam List<String> objects) {
return minioUtils.removeObjects(bucketName, objects);
}
}