MinIO介绍
MinIO 是一种构建高性能的云原生对象存储,基于机器学习,大数据分析,海量存储的架构进行设计,单个对象存储数据最大可达5TB,适合存储海量图片、视频、日志文件、备份数据和容器/虚拟机镜像等。MinIO主要采用Golang语言实现,基于Apache License v2.0开源协议,虽然轻量,却拥有着不错的性能,客户端与存储服务器之间采用http/https通信协议。其优点如下:
- 高性能
MinIO 是全球领先的对象存储先锋,目前在全世界有数百万的用户. 在标准硬件上,读/写速度上高达183 GB / 秒 和 171 GB / 秒。 - 可扩展性
MinIO利用了Web缩放器的来之不易的知识,为对象存储带来了简单的缩放模型。 这是我们坚定的理念 “简单可扩展.” 在 MinIO, 扩展从单个群集开始,该群集可以与其他MinIO群集联合以创建全局名称空间, 并在需要时可以跨越多个不同的数据中心。 通过添加更多集群可以扩展名称空间, 更多机架,直到实现目标。 - 云的原生支持
MinIO 是在过去4年的时间内从0开始打造的一款软件 ,符合一切原生云计算的架构和构建过程,并且包含最新的云计算的全新的技术和概念。 - 开放全部源代码 + 企业级支持
MinIO 基于Apache V2 license 100% 开放源代码 。 这就意味着 MinIO的客户能够自动的、无限制、自由免费使用和集成MinIO、自由的创新和创造、 自由的去修改、自由的再次发行新的版本和软件。 - 与Amazon S3 兼容
亚马逊云的 S3 API(接口协议) 是在全球范围内达到共识的对象存储的协议,是全世界内大家都认可的标准。 MinIO 在很早的时候就采用了 S3 兼容协议,并且MinIO 是第一个支持 S3 Select 的产品。 - 简单
极简主义是MinIO的指导性设计原则。简单性减少了出错的机会,提高了正常运行时间,提供了可靠性,同时简单性又是性能的基础。 只需下载一个二进制文件然后执行,即可在几分钟内安装和配置MinIO。
Windows安装使用MinIO
1、进入官网(https://min.io/download#/windows)进行下载
2、下载完成后是一个exe文件,新建一个目录用于存放数据
3、打开cmd窗口,切换到minio.exe目录下,运行如下命令
注意:该exe文件不能双击运行
minio.exe server ./MinIOData // ./MinIOData是存放数据的文件夹,需要自己指定
运行成功如下图
4、浏览器输入http://localhost:9000进行登录页面
输入网址http://localhost:9000,浏览器将自动跳转到http://localhost:9224/login
5、输入账号密码进行登录
系统默认登录用户名和密码都是 minioadmin,登录成功后进入系统主页
6、点击Create a Bucket创建一个目录进行存放文件
首先输入目录名称,minio系统称为水桶名称,创建成功后可以发现在本地数据文件目录下多了新建的文件夹。

7、上传、下载、分享
进入新建的Buckets,点击Upload,可以上传文件(Upload File)和上传目录(Upload Folder)

上传成功后,页面将显示上传文件的基本信息,勾选对应的文件,在页面右边可以进行下载、分享、查看和删除

点击share后,页面将会给你一个访问链接
http://169.254.205.236:9000/images/1.jpeg?X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=8RA23YUTTBA6I0W1FU17%2F20220726%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20220726T063539Z&X-Amz-Expires=604800&X-Amz-Security-Token=eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJhY2Nlc3NLZXkiOiI4UkEyM1lVVFRCQTZJMFcxRlUxNyIsImV4cCI6MTY1ODgxOTQ4NywicGFyZW50IjoiYWRtaW53cyJ9.U5sHwHOPT_QmO-qL-eBk8eJN2s15B9noJ3Pqb-YSgX3d5tqPhKAO8ruenIj-uVy_SdytTk3YB5e3zaMJK1DDww&X-Amz-SignedHeaders=host&versionId=null&X-Amz-Signature=ceda32bd33b653c76d3075674b7921c53d438620a5db87b5e7fbbde5a7f8a08f
此链接很长,只需要访问输入http://169.254.205.236:9000/images/1.jpeg即可访问,但是需要修改Buckets的访问权限,修改如下第八点。
- 而在本地文件夹下,则查看不了上传的文件,只能通过minio进行查看,本次存储的文件格式如下图

8、修改默认分享时间
minio系统默认分享时间是七天,七天后将查看不了文件。在分享页面可以通过修改时间来改变分享的时长,如果想永久访问就需要修改Buckets,也就是本目录的权限。
在创建新的Buckets时,系统默认分配private权限,创建成功后可以修改为public权限。步骤如下。将权限由private修改为public有两个优点:一是文件可以被永久访问,二是分享的链接可以很短

9、修改系统默认密码
修改系统配置文件config.json总是报错,不知道为啥
于是添加一个启动.bat文件进行修改,.bat文件内容如下,注意用户名和密码长度需要满足一定要求
set MINIO_ACCESS_KEY=adminabc
set MINIO_SECRET_KEY=12345678
minio.exe server MinIOData
centos安装MinIO
1、下载MinIO
wget https://dl.min.io/server/minio/release/linux-amd64/minio
2、设置执行权限
chmod 777 minio
3、添加数据文件目录
mkdir data
4、开发防火墙端口
firewall-cmd --zone=public --add-port=9000/tcp --permanent
firewall-cmd --reload
systemctl status firewalld # 查看防火墙状态
systemctl stop firewalld # 关闭防火墙
5、启动minIO
./minio server ./data

注意:这里出现了一个警告,意思是我的linux内核版本低于4.0.0,会出现一些性能问题,提示minio运行在4.0.0以上版本会更好。
启动成功后,操作就和Windows的操作一样,浏览器输入地址进行登录。
6、设置启动sh文件
export MINIO_ACCESS_KEY=admin # 登录用户名
export MINIO_SECRET_KEY=12345678 # 用户密码
# --address=":9001" 修改端口号
# --console-address ":9002" 设置控制台端口
# >> fileName 表示日志输出
# nohup ... & 表示后台运行,当前控制端页面关闭程序服务依然运行
nohup ./minio server /root/data --address=":9001" --console-address ":9002" >> info.log &
docker安装minIO
1、拉取镜像
docker pull minio/minio

2、运行minio镜像
添加一个启动镜像的sh文件,内容如下。/root/mminio/data:/data表示将目录文件与启动时数据文件进行映射,/root/mminio/config表示保存配置文件
docker stop minio
docker rm minio
docker run -it --name minio -p 9000:9000 -p 9001:9001 -d \
-e "MINIO_ROOT_USER=adminabc" \
-e "MINIO_ROOT_PASSWORD=12345678" \
-v /root/mminio/data:/data \
-v /root/mminio/config:/root/.minio \
minio/minio server /data --address ":9000" --console-address ":9001"
运行出现一个错误:
这是因为关闭防火墙导致问题,重新启动docker服务即可systemctl restart docker.service
重新运行启动minio镜像命令,即可成功启动
springboot整合MinIO
1、pom.xml文件引入依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.3.9</version>
</dependency>
2、yml文件配置
minio:
endpoint: http://loacalhost:9000
accessKey: adminws
secretKey: 12345678
bucketName: images
3、添加minio配置类
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* minio对象存储配置
*/
@Data
@Configuration
public class MinIOconfig {
@Value("${minio.endpoint}")
private String endpoint;
@Value("${minio.accessKey}")
private String accessKey;
@Value("${minio.secretKey")
private String secretKey;
/**
* 注入minio客户端
* @return
*/
@Bean
public MinioClient minioClient(){
return MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey,secretKey)
.build();
}
}
4、MinIO工具类
import io.minio.*;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
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.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
@Component
public class MinioUtils {
@Autowired
private MinioClient minioClient;
@Value("${minio.bucketName}")
private String bucketName;
/**
* 判断bucket是否存在
* @param bucketName bucket名称
* @return
*/
public Boolean existBucket(String bucketName) {
try {
boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
return exists;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 创建bucket
* @param bucketName 存储bucket名称
* @return Boolean
*/
public Boolean makeBucket(String bucketName) {
try {
minioClient.makeBucket(MakeBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 删除bucket
* @param bucketName bucket名称
* @return Boolean
*/
public Boolean removeBucket(String bucketName) {
try {
minioClient.removeBucket(RemoveBucketArgs.builder()
.bucket(bucketName)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 上传单个文件至 MinIo
* MultipartFile
* @param multipartFile 文件
* @param catalogue 上传文件保存的目录名
* @return 返回文件名
*/
public String uploadSingleFile(MultipartFile multipartFile, String catalogue){
String fileExtension = multipartFile.getOriginalFilename().substring(multipartFile.getOriginalFilename().lastIndexOf(".")+1);
List<String> arrayFileList = new ArrayList<>(Arrays.asList("jpeg","jpg","png","gif"));
if(!arrayFileList.contains(fileExtension)){
return "typeError";
}
long fileSize = multipartFile.getSize();
if (fileSize > 1024*1024*2){
return "sizeError";
}
String fileName = multipartFile.getOriginalFilename() + '_' + System.currentTimeMillis() + '.' + fileExtension;
String fileName1 = '/' + bucketName + '/' + fileName;
InputStream inputStream = null;
try{
inputStream = multipartFile.getInputStream();
minioClient.putObject(PutObjectArgs.builder()
.bucket(bucketName)
.object(fileName1)
.stream(inputStream,inputStream.available(), -1)
.contentType(multipartFile.getContentType())
.build()
);
} catch (Exception e) {
e.printStackTrace();
} finally{
if(inputStream != null){
try{
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return fileName;
}
/**
* 上传多个文件到 MinIo
* 文件类型:MultipartFile
* @param multipartFile 文件组
* @param catalogue 上传文件保存的目录名
* @return
*/
public List<String> uploadMultiFile(MultipartFile[] multipartFile, String catalogue) {
List<String> names = new ArrayList<>(multipartFile.length);
for (MultipartFile file : multipartFile) {
String fileName = uploadSingleFile(file, bucketName);
names.add(fileName);
}
return names;
}
/**
* 前端页面文件下载
* @param minIoFilePath minIo中下载的文件路径
* @param response 下载写入HttpServletResponse
* @return
* @throws Exception
*/
public boolean downloadResponse(String minIoFilePath, HttpServletResponse response) throws Exception{
try {
GetObjectResponse objectResponse = minioClient.getObject(GetObjectArgs.builder()
.bucket(bucketName)
.object(minIoFilePath)
.build());
response.setHeader("Content-Disposition","attachment;fileName=" + minIoFilePath.substring(minIoFilePath.lastIndexOf("/") + 1));
response.setContentType("application/force-download");
response.setCharacterEncoding("UTF-8");
IOUtils.copy(objectResponse,response.getOutputStream());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 上传本地文件
* @param path
* @return
*/
public String uploadLocalFile(String path) throws FileNotFoundException {
File file = new File(path);
String fileName = file.getName();
String fileExtension = file.getName().substring(file.getName().lastIndexOf(".") + 1);
List<String> arrayFileList = new ArrayList<>(Arrays.asList("jpeg","jpg","png","gif"));
if(!arrayFileList.contains(fileExtension)){
return "typeError";
}
if (file.length() > 1024*1024*2){
return "sizeError";
}
FileInputStream fileInputStream = new FileInputStream(file);
try {
minioClient.putObject(PutObjectArgs.builder()
.stream(fileInputStream,file.length(),PutObjectArgs.MIN_MULTIPART_SIZE)
.bucket(bucketName)
.object(fileName)
.build());
} catch (Exception e) {
e.printStackTrace();
return null;
}
return fileName;
}
/**
* 下载文件到本地
* @param minIoFilePath minIo中下载的文件路径
* @param localPath 下载到本地的文件路径
* @return
*/
public boolean downloadToLocal(String minIoFilePath, String localPath) throws Exception {
try {
minioClient.downloadObject(DownloadObjectArgs.builder()
.bucket(bucketName)
.object(minIoFilePath)
.filename(localPath)
.build());
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
/**
* 返回bucket中所有的文件名称,排除目录名
* @param bucketName
* @return
*/
public List<String> allFileName(String bucketName){
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).build());
List<String> filesName = new ArrayList<>();
try{
for (Result<Item> result : results){
Item item = result.get();
if (!item.isDir()){
filesName.add(item.objectName());
}
}
}catch (Exception e){
e.printStackTrace();
return null;
}
return filesName;
}
/**
* 删除bucket中的文件
* @param fileName 要删除的文件名
* @return
*/
public Boolean removeFile(String fileName) throws Exception{
try{
minioClient.removeObject(RemoveObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.build());
}catch (Exception e){
e.printStackTrace();
return false;
}
return true;
}
}
5、MinIO控制类
@Autowired
private MinioUtils minioUtilS;
@Value("${minio.endpoint}")
private String endpoint;
@Value("${minio.bucketName}")
private String bucketName;
@Resource
private MinioClient minioClient;
@PostMapping("/upload")
public String upload(MultipartFile file) {
String upload = minioUtilS.uploadSingleFile(file, "dir");
return upload;
}
