一、MinIO
(上传文件,下载文件)
MinlO是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据。例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。MinlO是一个非常轻量的服务 可以很简单的和其他应用的结合,类似NodeJS, Redis或者MySQL。
MinIO官网:MinIO | High Performance, Kubernetes Native Object Storage MinIO官方文档:
1、MinIO概述
1.1MinIO的优点
●部署简单:一个single二进制文件即是一切,还可以支持各种平台。
●minio支持海量存储,可按2one打展(原zone不受任何影响),支持单个对象最大5TB;
兼容Amazon S3接口,充分考虑开发人员的需求和体验;
●低冗余且磁盘损坏高容忍,标准且最高的数据冗余系数为2(即存储一个1 M的数据对象,实际占用磁盘空间为2M)。但在任意n/2块disk损坏的情况下依然可以读出数据(n为一个纠删码集合(Erasure Coding Set)中的disk数量)。并且这种损坏恢复是基于单个对象的,而不是基于整个存储卷的。
●读写性能优异
1.2、MinIO的基础概念
● Object: 存储到Minio的基本对象,如文件、字节流,Anything..
● Bucket:用来存储Object的逻辑空间。每个Bucket之间的数据是相互隔离的。对于客户端而言,就相当于一个存放文件的顶层文件夹。
● Drive: 即存储数据的磁盘,在MinIO启动时,以参数的方式传入。Minio 中所有的对象数据都会存储在Drive里。
● Set:即一组Drive的集合,分布式部署根据集群规模自动划分一 个或多个Set, 每个Set中的Drive分布在不同位置。一个对象存储在一个Set 上。(For example: {1..44} is divided into 4 sets each of size 16.)
● 一个对象存储在一 个Set上
● 一个集群划分为多个Set
● 一个Set包含的Drive数显是固定的,默认由系统根据集群规模自动计算得出
● 一个SET中的Drive尽可能分布在不同的节点上
1.3纠删码EC (Erasure Code)
MinIO使用纠删码机制来保证高可靠性,使用highwayhash来处理数据损坏( Bit Rot Protection)。关于纠删码,简单来说就是可以通过数学计算,把丢失的数据进行还原,它可以将n份原始数据,增加m份数据,并能通过n+m份中的任意n份数据,还原为原始数据。
即如果有任意小于等于m份的数据失效,仍然能通过剩下的数据还原出来。
1.4存储形式
文件对象上传到MinlO ,会在对应的数据存储磁盘中,以Bucket名称为目录,文件名称为下一级目录,文件名下是xl.meta,通过奇偶的形式存储 编码数据块及检验块和元数据文件。
如:
有data01,data02,data03,data04 四块盘
其中奇数盘:data01和data03中存储的是编码数据块及检验块(EC码)
偶数盘:data02和data04 存储的是元数据文件
1.5存储方案
2、windows系统部署MinIO
访问官网:MinIO Object Storage for Kubernetes — MinIO Object Storage for Kubernetes
进入官网之后点击右上角的DownLoad 进入下载页面 可以选择对应系统的安装包,我们下载windows版本的即可
windows版本下载之后是只有minio.exe文件,我们需要创建一个data文件,然后在打开cmd进入到minio.exe所在的文件夹中 执行
minio server ./data
执行完毕之后就是这样的 我们现在就可以访问自己电脑上的MinIO了
自习看之前的窗口 上面是有账号和密码的
RootUser: minioadmin
RootPass: minioadmin
3、Java整合MinIO
引入依赖:
<!-- MinIO --> <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.4.3</version> </dependency> <!-- okHttp:连接MinIO服务器(MinIO官方使用) --> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.9.0</version> </dependency>
官方API文档:Java Client API Reference — MinIO Object Storage for Linux
书写测试类:
/** * @Description:测试MinIO * @Author:DW * @Date:2023/10/9 16:37 */ @SpringBootTest public class MinIOTest { @Test void test1() throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { // 创建MinIO客户端 MinioClient client = MinioClient.builder() .endpoint("http://localhost:9000") .credentials("minioadmin", "minioadmin") .build(); // 判断要上传到的bucket是否存在,若不存在则创建 String bucketName = "test"; boolean found = client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); if (!found){ client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); }else{ System.out.println("【警告】名为" +bucketName+ "的bucket已经存在!!!"); } // 判断桶中的文件夹是否存在 // String dirName = "file/pic/"; // client.putObject(PutObjectArgs.builder() // .bucket(bucketName) // .object(dirName) // .stream(new ByteArrayInputStream(new byte[] {}), 0, -1) // .build()); // System.out.println("创建文件夹成功~~~"); // 上传文件 client.uploadObject(UploadObjectArgs.builder() .bucket(bucketName) // 指定上传到哪个桶 .object("file/pic/test3.jpg") // 指定上传之后的文件名(可以写携带路径,此时MinIO会根据路径自动创建文件夹) .filename("C:\\Users\\13237\\Pictures\\Saved Pictures\\image (3).jpg") // 被上传文件的路径 .build()); System.out.println("上传成功~~~"); } @Test void test2() throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException { // 创建MinIO客户端 MinioClient client = MinioClient.builder() .endpoint("http://localhost:9000") .credentials("minioadmin", "minioadmin") .build(); // 下载文件 client.downloadObject(DownloadObjectArgs.builder() .bucket("test") .object("file/pic/test3.jpg") .filename("C:\\Users\\13237\\Desktop\\test3.jpg") .build()); } }
minio的文件上传与删除
三. MinIO文件上传、删除
1. 引入依赖
<!-- minio --> <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.4.3</version> </dependency> <!-- okhttp,用于连接minio --> <dependency> <groupId>com.squareup.okhttp3</groupId> <artifactId>okhttp</artifactId> <version>4.9.0</version> </dependency>
2. 配置文件的编写
# 自定义minio的配置 minio: url: http://localhost:9000 accessKey: minioadmin secretKey: minioadmin # 文件上传的配置 shop-logo: bucketName: shop-logo
2. 配置类的编写
在base包中创建一个配置类
@Configuration public class MinIOConfig { @Value("${minio.url}") private String minioUrl; @Value("${minio.accessKey}") private String accessKey; @Value("${minio.secretKey}") private String secretKey; @Bean public MinioClient minioClient(){ return MinioClient.builder() .endpoint(minioUrl) // MinIO服务的URL .credentials(accessKey, secretKey) .build(); } }
3、MinIO的工具类
在base包的util子包中创建一个工具类
public class MinIOUtil { /** * 判断桶是否存在 * @param minioClient * @param bucketName * @return */ @SneakyThrows // 来自于lombok,用于在方法上自动生成异常声明(throws) public static boolean bucketExists(MinioClient minioClient, String bucketName) { return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); } /** * 先执行判断,若桶不存在则创建 * @param minioClient * @param bucketName */ @SneakyThrows public static void createBucketByCondition(MinioClient minioClient, String bucketName){ // 判断桶是否存在 boolean result = bucketExists(minioClient, bucketName); if (!result){ minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); }else{ System.out.println("名为:" + bucketName +" 的桶已存在!!!"); } } /** * 通过MultipartFile的IO流上传文件到MinIO * @param multipart * @param fileName * @param minioClient * @param bucketName */ public static boolean uploadFile(MultipartFile multipart, String fileName, MinioClient minioClient, String bucketName){ try { minioClient.putObject(PutObjectArgs.builder() .bucket(bucketName) .object(fileName) .stream(multipart.getInputStream(), multipart.getSize(), -1) .contentType(multipart.getContentType()) .build()); return true; }catch (Exception e){ e.printStackTrace(); return false; } } /** * 删除MinIO中的文件 * @param fileName * @param minioClient * @param bucketName * @return */ public static boolean deleteFile(String fileName, MinioClient minioClient, String bucketName){ try { minioClient.removeObject(RemoveObjectArgs.builder() .bucket(bucketName) .object(fileName) .build()); return true; }catch (Exception e){ e.printStackTrace(); return false; } } }
4. 上传、删除接口
在base包中创建一个controller子包,并在该包中创建MinIOController
@RestController @RequestMapping("/minio") public class MinIOController { @Resource MinioClient minioClient; @Value("${shop-logo.bucketName}") private String bucketName; @PostMapping public AjaxResult upload(MultipartFile file){ // 有条件地创建桶 MinIOUtil.createBucketByCondition(minioClient, bucketName); String fileName = UUID.randomUUID() + "_" +file.getOriginalFilename(); boolean result = MinIOUtil.uploadFile(file, fileName, minioClient, bucketName); if (result){ // 上传成功则响应文件名给前端 return AjaxResult.me().setSuccess(true).setMessage(fileName); } return AjaxResult.me().setSuccess(false).setMessage("上传失败!!!"); } @DeleteMapping public AjaxResult delete(String fileName){ boolean result = MinIOUtil.deleteFile(fileName, minioClient, bucketName); if(result){ return AjaxResult.me().setSuccess(true).setMessage("删除成功!!!"); } return AjaxResult.me().setSuccess(false).setMessage("删除失败!!!"); } }