一、Minio基础知识
官方说明
Minio是一款高性能的对象存储服务器,它兼容Amazon S3 API。它的设计目的是为了提供云存储服务的性能和可扩展性,同时还保持着本地存储的简单性和易用性。Minio可以在Linux、MacOS和Windows等操作系统上运行,它可以通过命令行界面或RESTful API进行管理。
Minio的核心是对象存储,对象是一组二进制数据和元数据的组合。对象可以存储为文件,也可以存储为内存中的数据结构。对象可以存储在不同的存储介质中,如本地磁盘、网络文件系统、云存储等。Minio支持多种存储介质,它可以轻松地将数据存储到本地磁盘、Amazon S3、Google Cloud Storage、Microsoft Azure Blob Storage等云存储服务中。
Minio的架构采用了分布式的设计,它可以将数据分散存储在多个节点中,从而实现数据的高可用和容错性。在Minio中,节点被称为Minio Server,每个Minio Server可以存储一个或多个对象存储桶。对象存储桶是一组对象的集合,类似于文件系统中的文件夹。每个对象存储桶都有一个唯一的名称,它可以在Minio集群中全局唯一。
Minio的数据访问是通过RESTful API实现的,它可以提供各种数据管理功能,如创建、删除、读取、写入对象等。Minio的API与Amazon S3 API兼容,这意味着您可以使用Amazon S3 SDK和工具来与Minio交互。
MinIO是一款基于Go语言发开的高性能、分布式的对象存储系统。客户端支持Java,Net,Python,Javacript, Golang语言。
MinIO 英文官网 MinIO 中文官网 注意:中文官方更新不及时,会有很多坑,请以英文官网为准。
基础概念
1、Object:存储到minio的基本对象,如文件,字节流,Anything。。。。
2、Bucket:用来存储Object的逻辑空间。每个Bucket之间的数据是互相隔离的。对于客户端而言,就相当于存放文件的顶层文件夹。
3、Drlve:存储数据的磁盘,在MinIO启动时,以参数的方式传入。MinIO中所有的对象数据都会存在Drive里。
4、Set:即一组 Drive的集合,分布式部署根据集群规模自动划分一个或者多个Set,每个Set中的Drive 分布在不同位置。一个对象存储在一个Set上。
4.1、一个对象存储在一个Set上。
4.2、一个集群划分为多个Set。
4.3、一个Set包含的Drive数量是固定的,默认由系统根据集群规模自动计算得出。
4.4、一个Set中的drive 尽可能分布在不同的节点上。
5、纠删码:Minio 使用纠删码机制来保证高可靠性。
5.1、使用highwayhash来处理数据损坏(Bit Rot Protectio)。关于纠删码,简单来说就是可以通过数学计算,把丢失的数据进行还原。
5.2、纠删码是恢复丢失和算怀数据的数学算法,minio采用reed-solomincode将对象拆分成N/2数据和N/2奇偶校验块。这就意味着如果是12块盘,一个对象会被分成6个数据快、6个奇偶校验块,你可以丢失任意6块盘(不管其存放的是数据块还是奇偶校验块),你任可以从剩下的盘中的数据进行恢复。
5.2、纠删码模式,把数据分成 DATA BLOCk:数据块、 PARITY BLOCK:校验块。一个BLOCK 10M左右,如果一个文件小于10M,就以文件大小进行分块。如果一个文件大于10m,就以10M为一个文件单位进行分块。
应用场景
1、互联网非结构化数据的存储需求
1.1 、电商网站:海量商品图片
2.1、视频网站:海量视频文件
3.1、网盘:海量文件
MinIO优势
1、数据保护
1.1、分布式MInio采用纠删码来放反多个节点待机和位衰减 bit rot
1.2、分布式Minio至少需要4个硬盘,使用分布式Minio自动引入了纠删码功能。
2、高可用
2.1、单机Minio服务存在单点故障,相反,如果是有一个N快硬盘的分布式Minio,只要有N/2硬盘在线,你的数据就是安全的。不过你需要至少有N/2+1个硬盘来创建新的对象。
2.2、例如,一个16节点的Minio集群,每个节点16块块硬盘,计算8台服务器宕机,这个集群仍然是可读的,不过你需要9台服务器才能写入数据。
3、一致性
3.1、Minio在分布式和单机模式下,所有读写炒作都严格遵守read-after-write 一致性模型
二、安装MinIO(CentOS7)
1
| wget https://dl.minio.org.cn/server/minio/release/linux-amd64/minio
|
1
| cp minio /usr/local/minio/
|
1 2 3 4 5 6
| export MINIO_ROOT_USER=root export MINIO_ROOT_PASSWORD=cqc123456
./minio server --console-address 0.0.0.0:9111 /usr/local/minio/data > /usr/local/minio/minio.log 2>&1 &
|
三、Springboot整合
1.先去管理页面创建Buckets(桶)

2.导入依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>
<dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.5.6</version> </dependency>
<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> <version>3.12.0</version> </dependency> </dependencies>
|
3.项目配置(application.properties)
1 2 3 4 5 6 7 8 9 10 11 12
| server.port=9091
minio.endpoint = http://192.168.55.180:9000 minio.accessKey = root minio.secretKey = cqc123456 minio.bucketName = cqc
spring.servlet.multipart.max-file-size=1000MB spring.servlet.multipart.max-request-size=5000MB
|
4.代码部分
ResponseData.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
| package com.cactus.minio.common;
public class ResponseData {
private int code; private String msg; private Object data;
public static ResponseData error(String msg) { return new ResponseData(1500,msg,null); }
public ResponseData(int code, String msg, Object data) { this.code = code; this.msg = msg; this.data = data; }
public static ResponseData success(Object urlList) { return new ResponseData(200,"success",urlList); }
public int getCode() { return code; }
public void setCode(int code) { this.code = code; }
public String getMsg() { return msg; }
public void setMsg(String msg) { this.msg = msg; }
public Object getData() { return data; }
public void setData(Object data) { this.data = data; } }
|
MinioConfig.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| package com.cactus.minio.config;
import io.minio.MinioClient; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component;
@Data @Component @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(); } }
|
FileController.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
| package com.cactus.minio.controller;
import com.cactus.minio.common.ResponseData; import io.minio.*; import io.minio.http.Method; import org.apache.commons.lang3.StringUtils; import org.apache.tomcat.util.http.fileupload.IOUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile;
import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.InputStream; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map;
@RestController @RequestMapping("/file") public class FileController { @Resource private MinioClient minioClient;
@Value("${minio.bucketName}") private String bucketName;
@PostMapping("/upload") public ResponseData upload(@RequestParam(name = "file") MultipartFile[] file){ if(file == null || file.length ==0){ return ResponseData.error("上传文件不能为空"); } if(file.length > 10){ return ResponseData.error("请上传少于" + file.length + "个文件"); } try { List<String> urlList = new ArrayList<>(file.length); for (MultipartFile multipartFile : file){ InputStream in = multipartFile.getInputStream();
minioClient.putObject(PutObjectArgs.builder().object(multipartFile.getOriginalFilename()) .bucket(bucketName) .contentType(multipartFile.getContentType()) .stream(in, multipartFile.getSize(),-1).build()); in.close(); String str = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder() .bucket(bucketName) .object(multipartFile.getOriginalFilename()) .method(Method.POST) .build()); String urlStr = StringUtils.substringBefore(str, "?"); urlList.add(urlStr); } Map<String, Object> map = new HashMap<>(); map.put("urlList", urlList); return ResponseData.success(map.get("urlList")); } catch (Exception e) { return ResponseData.error("上传失败:" + e.getMessage()); } }
@GetMapping("/download/{filename}") public void download(@PathVariable String filename, HttpServletResponse response){ InputStream in = null; try { StatObjectResponse stat = minioClient.statObject( StatObjectArgs.builder().bucket(bucketName).object(filename).build()); response.setContentType(stat.contentType()); response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(filename, "utf-8")); in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(filename).build()); IOUtils.copy(in, response.getOutputStream()); }catch (Exception e){ e.getMessage(); }finally { if(in != null){ try { in.close(); }catch (IOException e){ e.getMessage(); } } } }
@DeleteMapping("/delete/{filename}") public ResponseData delete(@PathVariable("filename") String filename){ try { minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName) .object(filename).build()); }catch (Exception e){ e.getMessage(); return ResponseData.error("删除失败"); } return ResponseData.success("删除成功"); } }
|
四、测试
1.上传文件
1
| POST http://localhost:9091/file/upload
|

2.下载文件
1
| GET http://localhost:9091/file/download/增强功力.pdf
|
3.删除文件
1
| DELETE http://localhost:9091/file/delete/增强功力.pdf
|

五、总结
- MinIO用Buckets的概念进行分区,找一个文件 需要Buckets+文件名
- 个人感觉MinIO比Hadoop、FastDFS无论在安装还行运维还是整合方面都是占优的
- 上传到MinIO文件可以通过官方api合并文件(分片合并操作),也可以创建Buckets,存在大量api
