0. 引言
在实际开发中,我们经常会面临需要存储文档、存储图片等文件存储需求,并且在分布式架构下,文件又需要实现各节点共享,类似于共享文件夹类的需求,在分布式服务器中创建共享文件夹成本较大,甚至当需要跨机房访问时就不满足了,此时我们需要一个第三方的组件,来实现这类对象存储
也就出现了OSS、OBS、minIO、hdfs这类对象存储组件,今天,我们主要来学习免费开源的MinIO组件
1. minio简介
MinIO是一个使用go语言开发的,开源的对象存储组件,能够提供高性能、高可用的数据存储能力,支持分布式部署,提供数据加密、访问控制、版本控制、生命周期管理和事件通知等功能。它还支持高级特性,如分片上传和分片下载,以提高大文件的处理效率。
官方文档:https://www.minio.org.cn/
2. minio安装
minio支持docker安装,压缩包安装,这里我们为了安装方便,采用docker安装,如果使用的是mac,也可以使用brew工具安装,具体可参考官网文档:https://github.com/minio/minio
1、下载镜像
docker pull minio/minio
2、创建数据映射目录
mkdir -p /Library/software/dockerdata/minio/data
3、创建容器
注意这里我安装的版本是RELEASE.2023-01-02T09-40-09Z
docker run -p 9000:9000 -p 9090:9090 \--name minio \-e "MINIO_ACCESS_KEY=minioadmin" \-e "MINIO_SECRET_KEY=minioadmin" \-v /Library/software/dockerdata/minio/data:/data \minio/minio server \/data --console-address ":9090" -address ":9000"
4、登陆 localhost:9090
,输入账号/密码: minioadmin / minioadmin
3. minio管理端介绍
管理端默认通过9090端口进入:http://ip:9090/
其中比较常用的菜单包括:
- Object Browser: 对象管理页面,minio中所有的桶和桶中的文件都可以在这个页面查看,这里的桶 bucket,大家可以简单的理解为文件夹
- Buckets: 桶管理页面,用于管理桶相关的配置,比如桶的访问权限、桶的生命周期(桶中文件保留几天)
- Identity: 权限管理页面,可以创建用户、分组,并设置对应权限等
- Monitoring: 监控页面,监控显示minio的各类健康、状态、日志信息
4. minio客户端使用
1、添加pom依赖
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.5.3</version>
</dependency><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.10.0</version>
</dependency>
2、增加配置文件
minio:# minio地址endpoint: http://localhost:9000# 账户username: minioadmin# 密码password: minioadmindefaultBucketName: test
3、创建配置类,用于生成MinioClient
/*** @author benjamin_5* @Description minio配置类* @date 2023/8/5*/
@Configuration
public class MinioConfig {@Value("${minio.endpoint}")private String endpoint;@Value("${minio.username}")private String username;@Value("${minio.password}")private String password;@Value("${minio.defaultBucketName}")private String defaultBucketName;@Beanpublic MinioClient minioClient(){return MinioClient.builder().credentials(username, password).endpoint(endpoint).build();}}
4、创建一个返回实体类,方便规范返回信息
@Data
public class MinioReturn {/*** 文件地址*/private String path;/*** 原始文件名*/private String inputName;/*** 最终文件名*/private String outPutName;}
5、创建MinioTemplate
类,用来书写minio工具类
@Component
public class MinioTemplate {@Autowiredprivate MinioClient minioClient;private static final String SLASH = "/";@Value("${minio.defaultBucketName}")private String defaultBucketName;@Value("${minio.endpoint}")private String endpoint;/*** 创建桶** @param bucketName* @throws Exception*/public void makeBucket(String bucketName) throws Exception {BucketExistsArgs args = BucketExistsArgs.builder().bucket(bucketName).build();if (!minioClient.bucketExists(args)) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());}}/*** 上传文件** @param file* @return* @throws Exception*/public MinioReturn putFile(MultipartFile file) throws Exception {return putFile(file, file.getOriginalFilename(), defaultBucketName);}public MinioReturn putFile(MultipartFile file, String fileName, String bucketName) throws Exception {if (bucketName == null || bucketName.length() == 0) {bucketName = defaultBucketName;}makeBucket(bucketName);minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build());return new MinioReturn(fileLink(bucketName, fileName), file.getOriginalFilename(), fileName);}/*** 删除文件** @param bucketName* @param fileName* @throws Exception*/public void removeFile(String bucketName, String fileName) throws Exception {minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName == null || bucketName.length() == 0 ? defaultBucketName : bucketName).object(fileName).build());}@SneakyThrowsprivate String fileLink(String bucketName, String fileName) {return endpoint.concat(SLASH).concat(bucketName).concat(SLASH).concat(fileName);}private String getFileName(String fileName) {return getFileName(null, fileName);}private String getFileName(String prefix, String fileName) {String fileNamePre = fileName;String fileType = "";int index = fileName.lastIndexOf(".");if (index > 0) {fileNamePre = fileName.substring(0, index);fileType = fileName.substring(index);}String name = UUID.randomUUID().toString().replace("-", "");if (!org.springframework.util.StringUtils.isEmpty(fileNamePre)) {name = fileNamePre + "-" + name + fileType;}if (!StringUtils.isEmpty(prefix)) {name = prefix + "-" + name;}return name;}}
6、书写控制类,用于测试
@RestController
@RequestMapping("minio")
@AllArgsConstructor
public class MinioController {private final MinioTemplate minioTemplate;@PostMapping("/upload")@ResponseBodypublic MinioReturn upload(MultipartFile file) throws Exception {return minioTemplate.putFile(file);}@PostMapping("/remove")@ResponseBodypublic String remove(String fileName, String bucketName) throws Exception{minioTemplate.removeFile(bucketName, fileName);return "success";}}
7、调用上传接口,如下图所示,调用成功
我们查看minio中,自动创建了桶,并且文件也上传成功了
同样再测试一下删除接口
查看minio中删除成功
5. 应用场景
-
- 静态资源存储
如在官网、门户、首页等网站,我们经常需要加载一些静态资源,如图片、js文件、视频文件等,一些我们可以直接存放到nginx加载,另一方面我们也可以存放到minio中,通过minio进行访问
要通过minio访问的前提是记得把桶权限设置为public
上述代码创建的桶默认是private
的,如果想要通过客户端代码调整桶权限的话,可以通过minioClient.setBucketPolicy
方法,设置完后可以通过返回的地址进行访问,如下所示(如果想要通过外网访问,给对应的内网地址端口做个外网映射即可)
开启权限public会有个安全问题,那就是当你访问桶路径时,会发现会把所有桶下的文件列出来,这样只要再拼接上文件名就能访问所有文件了
要解决这个问题,只需要将权限设置为custom
,然后将"s3:ListBucket"
取消即可
再次访问会发现权限禁止,而加上文件名后是可以正常查看的
当然如果在旧版本中是不支持直接在minio管理页面中直接设置的,你可以通过下载s3browser https://s3browser.com/,连接上minio后,右键在Edit Bucket Policy
中设置
-
- 文件暂存
某些时候,涉及组件或系统间传输文件时,直接通过接口传输效率太慢且及时性不足,或者用户其实收到后不需要马上查看这些文件,这时我们就可以把文件暂存到minio中,发送一个文件地址给客户,客户收到这个地址可以再进行下载。
既然是文件暂存,那么就希望能自动删除,实现自动删除有两种方法,一种是通过书写shell脚本,直接删除指定路径下的文件,minio在服务器上存储的文件就是直接放在文件夹下,没有特殊处理,所以直接删除即可,第二种就是使用minio提供的生命周期管理,在Buckets-Lifecycle中可以设置,同时这个也可以在客户端中创建桶时就设置,通过minioClient.setBucketLifecycle();
方法
-
- 解放带宽压力
某些时候,涉及接口文件传输时,直接传输占用大量带宽,导致并发上不去,而客户可能也不需要马上使用到传输的文件,只是需要马上知道一个状态或者其他信息,这时就可以通过将文件上传到minio, 而发送一个文件地址给客户,这样将文件状态通知和下载分步进行。
这里某的同学可能会想,下载不还是要占用带宽吗,怎么说是节约带宽资源呢,这是因为一般接口调用追求耗时,我们一般会把接口服务的带宽部署为性能更高的,价格自然更贵,而文件下载服务的没有那么高的延迟要求,就单独拉一根性能相对没那么强的,也就更便宜的,这样自然减少了带宽费用
6. 总结
至此,我们针对minio的快速上手就完成了, 文中只是描述了minio最常用的方法,还有更多方法、指令等待大家自己探索学习。
文中演示源码见如下地址:https://gitee.com/wuhanxue/wu_study/tree/master/demo/minio_demo