一、Minio介绍:
目前可用于文件存储的网络服务选择也有不少,比如阿里云OSS、七牛云、腾讯云等等,可是收费都有点小贵。为了省钱,很多公司使用MinIO做为文件服务器。
官网:https://www.minio.org.***/
MinIO是一个开源的分布式对象存储服务器,支持S3协议并且可以在多节点上实现数据的高可用和容错。它采用Go语言开发,拥有轻量级、高性能、易部署等特点,并且可以自由选择底层存储介质。它基于Apache License 开源协议,兼容Amazon S3云存储接口。适合存储非结构化数据,如图片,音频,视频,日志等。
二、Minio的下载:
有Windows和Linux两种方式,不过我们一般把服务器下载到Linux中。使用docker来部署,能够很方便的管理(默认你的Linux中已经下载好docker了)。
//拉取镜像
docker pull quay.io/minio/minio// 创建数据存储目录
mkdir -p ~/minio/data// 创建minio
docker run \
-p 9001:9000 \
-p 9090:9090 \
--name minio \
-v ~/minio/data:/data \
-e "MINIO_ROOT_USER=admin" \
-e "MINIO_ROOT_PASSWORD=admin123456" \
-d \
quay.io/minio/minio server /data --console-address ":9090"
在这里解释一下这个命令:
docker pull 拉取Minio的镜像,我没有指定版本,所以默认下载最新版本。
mkdir -p ~/minio/data:创建一个文件夹来存储我们上传到Minio中的文件。
实际上,我们上传到Minio中的文件都存储到了这个文件夹中。也就是说我们上传到Minio中的文件其实都保存在你的Linux服务器中。
docker run :创建容器并运行。解释一下重要的几个参数。
9001:Minio的服务端端口
9090:Minio的客户端端口
-v ~/minio/data:/data :数据卷挂载我们之前创建的文件夹,使之成为Minio的存储容器
-e "MINIO_ROOT_USER=admin" :用户名为admin
-e "MINIO_ROOT_PASSWORD=admin123456" :密码为admin123456
将容器运行起来:
现在登录我们的客户端,并输入用户名和密码:
http://Ip地址:9090
我们还要了解一下“桶”在Minio中的概念:
"桶"(Bucket)是用来组织和管理存储的对象(文件或文件夹)的基本单位。你可以把它想象成一个容器,用来存放你的对象。
以下是关于桶的一些重要概念:
-
命名:每个桶都有一个全局唯一的名称。这意味着在同一个MinIO服务中,不能有两个名称相同的桶。
-
隔离:桶之间是完全隔离的。一个桶中的对象不能直接访问另一个桶中的对象。
-
访问控制:每个桶都可以有自己的访问控制策略。例如,你可以设置一个桶为公开,任何人都可以读取它的内容;也可以设置一个桶为私有,只有特定的用户可以访问它的内容。
-
无限制的对象存储:每个桶内可以存储无限数量的对象,只要你的存储空间足够。
-
元数据:每个桶都可以有一些元数据,如创建时间、修改时间等。
可以选择在客户端创建桶,也可以使用Java代码来创建,我这里就使用Java代码创建桶了。
三、使用spring boot整合Minio,完成文件的上传、下载和删除。
版本:spring boot3、jdk17、Minio的版本为:RELEASE.2023-11-01T18-37-25Z
本次使用knife4j进行测试;
创建一个空的spring boot项目,并引入如下依赖:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>***.github.xiaoymin</groupId> <artifactId>knife4j-openapi3-jakarta-spring-boot-starter</artifactId> <version>4.3.0</version> </dependency> <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.5.2</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency></dependencies>
在yml配置文件中引入如下配置:
minio: url: http://192.168.231.110:9001 username: admin password: admin123456 bucketName: test spring: servlet: multipart: max-file-size: 10MB # 单个文件上传的最大上限 max-request-size: 100MB # 整个请求体的最大上限
minio:我们自定义的属性
url:你的服务端地址
username:用户名
password:密码
bucketName:test(桶的名称,我们现在没有创建,一会用Java代码实现)
max-file-size:一定要配置这个属性,不然springboot默认的单个文件上传的最大上限为1MB,超过这个上限,就会报错。
1、创建一个实体类来继承yml文件中minio的连接信息;
@ConfigurationProperties(prefix = "minio") @***ponent @Data public class MinioPojo { private String url; private String username; private String password; private String bucketName; }
2、自定义bean,将MinioClient初始化;
@Configuration public class MinioConfig { @Autowired private MinioPojo minioPojo; @Bean public MinioClient minioClient(){ return MinioClient.builder() .endpoint(minioPojo.getUrl()) //传入url地址 //传入用户名和密码 .credentials(minioPojo.getUsername(), minioPojo.getPassword()) .build(); //完成MinioClient的初始化 } }
3、新创建一个MinioService类,用来进行文件的操作,并注册为bean。将来在这里进行具体代码的编写。
@***ponent public class MinioService { @Autowired private MinioClient minioClient; @Autowired private MinioPojo minioPojo;}
4、新建一个Controller,用来进行文件的上传。
@RestController @RequestMapping("/tests") public class TestController { @Autowired private MinioService minioService; @Autowired private MinioPojo minioPojo; //文件上传 @Operation(summary = "上传图片") @PostMapping("/uploadImage") public String aa(MultipartFile file){ String url = minioService.uploadImage(minioPojo.getBucketName(), file); return url; }}
我们在MinioService中定义了uploadImage方法,传入了两个参数。一个是桶的名称,一个是文件file。(注意参数名称一定要叫file,因为现在前端默认传文件时都会叫这个名字,如果你随意的更改名字,后端会接收不到参数。)
返回文件在Minio中的地址url。(注意,只要你的容器Minio还在运行,就可以直接访问到。)
5、在MinioService中进行方法的实现。
public String uploadImage(String bucketName, MultipartFile file) { try { //判断桶是否存在 boolean bucketExists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); if (!bucketExists){ // 如果不存在,就创建桶 minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); } // 本地时间,具体到年、月、日 String yyyymmdd = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd")); // String uuid= UUID.randomUUID().toString(); String filename = yyyymmdd+"/"+file.getOriginalFilename(); // 加一个/表示创建一个文件夹 minioClient.putObject(PutObjectArgs.builder(). bucket(bucketName). object(filename). stream(file.getInputStream(), file.getSize(), -1). // 文件上传的类型,如果不指定,那么每次访问时都要先下载文件 contentType(file.getContentType()). build()); String url= minioPojo.getUrl()+"/"+minioPojo.getBucketName()+"/"+filename; return url; } catch (Exception e) { e.printStackTrace(); throw new RuntimeException("文件上传失败"); } }
现在,我们可以在knife4j中去测试发送信息了。
如图所示,我们发送文件成功了。返回了一个url地址,我们输入这个地址就可以直接访问到这张图片了
(注意,这里有坑。我们虽然能够上传文件,并返回了文件在Minio中的地址,但是我们现在并不能直接去访问,因为现在这个桶test默认的访问权限不够,我们要在Minio的客户端将这个桶的访问权限设为public。),创建存储桶时无法直接设置为公开访问,你需要在创建存储桶后再设置存储桶的策略来实现公开访问。这是因为MinIO遵循最小权限原则,以确保数据安全。
在进行文件的上传时,如果文件名称相同会进行覆盖。这时一般的方法为在每一次文件上传时,对文件生成一个UUID,以保证每一次上传的文件名称都不一样,以免覆盖。
也可以在Java代码中实现桶访问权限的修改,但是很麻烦。
// 设置存储桶的策略
String policyJson = "{\"Statement\"[{\"Action[\"s3:GetObject\"],\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"],\"Sid\":\"\"}],\"Version\":\"2012-10-17\"}"; minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(policyJson).build());
现在,我们就可以直接输入返回的url地址,来查看图片并下载。
我这次上传的文件类型是图片,其他的文件类型也可以上传。
6、实现文件的下载:
public String getObject(String url){ try{ InputStream inputStream = minioClient.getObject(GetObjectArgs.builder() .bucket(minioPojo.getBucketName()) .object(url) .build()); // 将输入流的内容复制到一个文件中 Files.copy(inputStream, Path.of("D://test.png"), StandardCopyOption.REPLACE_EXISTING); return "文件上传成功"; }catch (Exception e){ e.printStackTrace(); } return "文件上传失败"; }
这里传入的url是在Minion,相对于桶的路径
假如现在在Minio中有一个url地址:http://192.168.231.110:9001/test/2023-11-05/32***63cb-eb0c-4398-8bb8-3ac37c3a130410.png
url:http://192.168.231.110:9001
bucketName:test
要传入的url参数为:
2023-11-05/32***63cb-eb0c-4398-8bb8-3ac37c3a130410.png
7、实现文件的删除:
public void deleteFile(String bucketName,String url) { try { minioClient.removeObject(RemoveObjectArgs.builder() .bucket(bucketName) .object(url) .build()); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(url+"文件删除失败"); } }
文件的删除,要传入两个参数。一个是文件所在桶的名称,另一个是文件在Minio中,相对于桶的路径:现在在Minio中有一个url地址:http://192.168.231.110:9001/test/2023-11-05/32***63cb-eb0c-4398-8bb8-3ac37c3a130410.png
url:http://192.168.231.110:9001
bucketName:test
要传入的url参数为:
2023-11-05/32***63cb-eb0c-4398-8bb8-3ac37c3a130410.png
运行这个方法是在minio中将图片该删除掉了。
总结:
我们使用minio的常用操作就是,上传、下载和删除。
这些文件都存储在我们的Linux服务器中,并不能凭空存在,所以上传时要注意把握力度。如果上传的文件太多太大的话,Linux会先撑不住的。