MinIO文件服务器的安装与使用
文章目录
一、 MinIO简介
1.1 MinIO是什么?
MinIO是基于Apache License v2.0开源协议的对象存储服务
,可以做为云存储的解决方案用来保存海量的图片,视频,文档。由于采用Golang实现,服务端可以工作在Windows,Linux, OS X和FreeBSD上。配置简单,基本是复制可执行程序,单行命令可以运行起来。
MinIO兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
S3 ( Simple Storage Service简单存储服务):S3协议
是Amazon Web Services (AWS) 提供的对象存储服务(Simple Storage Service)的API协议。它是一种 RESTful风格的Web服务接口,使用HTTP/HTTPS协议进行通信,支持多种编程语言和操作系统,并实现了数据的可靠存储、高扩展性以及良好的可用性。
基本概念:
- bucket – 类比于文件系统的目录
- Object – 类比文件系统的文件
- Keys – 类比文件名
1.2 MinIO的特点
-
数据保护
Minio使用Minio Erasure Code(纠删码)来防止硬件故障。即便损坏一半以上的driver,但是仍然可以从中恢复。
-
高性能
作为高性能对象存储,在标准硬件条件下它能达到55GB/s的读、35GB/s的写速率
-
可扩容
不同MinIO集群可以组成联邦,并形成一个全局的命名空间,并跨越多个数据中心
-
SDK支持
基于Minio轻量的特点,它得到类似Java、Python或Go等语言的sdk支持
-
有操作页面
面向用户友好的简单操作界面,非常方便的管理Bucket及里面的文件资源
-
功能简单
这一设计原则让MinIO不容易出错、更快启动
-
丰富的API
支持文件资源的分享连接及分享链接的过期策略、存储桶操作、文件列表访问及文件上传下载的基本功能等。
-
文件变化主动通知
存储桶(Bucket)如果发生改变,比如上传对象和删除对象,可以使用存储桶事件通知机制进行监控,并通过以下方式发布出去:AMQP、MQTT、Elasticsearch、Redis、NATS、MySQL、Kafka、Webhooks等。
二、MinIO的安装
2.1 docker部署MinIO命令
#拉取镜像
docker pull quay.io/minio/minio
#创建数据存储目录
mkdir -p /home/minio/data
#创建minio
docker run \
-p 9000:9000 \
-p 9090:9090 \
--name minio \
--restart=always \
-v /home/minio/data:/data \
-e "MINIO_ROOT_USER=admin" \
-e "MINIO_ROOT_PASSWORD=admin123456" \
-d \
quay.io/minio/minio server /data --console-address ":9000" --address ":9090"
参数解释:
-p
:宿主机端口映射到容器内端口(- p 宿主机端口: 容器内部端口), 900
--name minio
::指定容器的名称为 minio;
--restart=always
: 设置容器在退出后自动重新启动。
-v
:数据挂在(-v 本地目录 : 容器内目录);
-e
::设置环境变量(-e KEY=VALUE),设置用户名MINIO_ROOT_USER
以及密码MINIO_ROOT_PASSWORD
;
-d
: 是让容器在后台运行
--console-address
: 指定容器的控制台地址。这里设置为 :9000,表示可以通过宿主机上的 9000 端口访问容器的控制台。
-address
: 指定容器的网络地址。这里设置为 :9090,表示可以通过宿主机上的 9090 端口访问容器的服务。
2.2 MinIO客户端
容器创建完成后,注意开放服务器防火墙,测试访问:ip:9000,输入用户名、密码进入客户端界面
创建bucket
修改Access Policy为public
三、MinIO的使用
3.1 添加minio依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>7.1.0</version>
</dependency>
测试上传代码:
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import java.io.FileInputStream;
public class FileUploadTest {
public static void main(String[] args) throws Exception {
// 创建一个Minio的客户端对象
MinioClient minioClient = MinioClient.builder()
.endpoint("http://你的ip:9090")
.credentials("admin", "admin123456")
.build();
// 查询桶是否存在
boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket("test-bucket").build());
// 如果不存在,那么此时就创建一个新的桶
if (!found) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket("test-bucket").build());
} else { // 如果存在打印信息
System.out.println("Bucket 'test-bucket' already exists.");
}
FileInputStream fis = new FileInputStream("D://piggy.png") ;
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.bucket("test-bucket")
.stream(fis, fis.available(), -1)
.object("piggy.png")
.build();
minioClient.putObject(putObjectArgs) ;
}
}
3.2 配置MinIO
将MinioClient的配置信息写入配置文件application.yml
中
# minio相关配置
minio:
endpoint: http://你的ip:9090
accessKey: admin
secretKey: admin123456
bucketName: test-bucket
MinioProperties
读取配置文件中的minio配置信息
@Data
@ConfigurationProperties(prefix="minio")
public class MinioProperties {
private String endpoint;
private String accessKey;
private String secretKey;
private String bucketName;
}
配置类MinioConfig
配置minioClient并放入容器中
@Data
@Configuration
@EnableConfigurationProperties({MinioProperties.class})
public class MinioConfig {
@Autowired
private MinioProperties minioProperties;
@Bean
public MinioClient buildMinioClient(){
return MinioClient
.builder()
.credentials(minioProperties.getAccessKey(), minioProperties.getSecretKey())
.endpoint(minioProperties.getEndpoint())
.build();
}
}
3.3 MinIO文件操作
3.3.1 上传文件
MinIOFileService
public interface MinioFileService {
/**
* 上传图片文件
* @param filePath 待上传文件路径
* @param targetFolder 文件要上传到minio的位置
* @param filename 文件名
* @return 文件全路径
*/
public String uploadFile(String filePath, String targetFolder, String filename);
public String upload(MultipartFile multipartFile, String targetFolder, String filename);
}
MinioFileServiceImpl
判断桶是否存在:minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
创建桶子:minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
上传文件参数设置:PutObjectArgs putObjectArgs = PutObjectArgs.builder().bucket(bucketName).stream(fis, fis.available(), -1).object(objectName).build();
上传文件:minioClient.putObject(putObjectArgs) ;
package com.cxj.minio.service.impl;
import com.cxj.minio.config.MinioProperties;
import com.cxj.minio.service.MinioFileService;
import io.minio.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.UriUtils;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* ClassName: MinioFileServiceImpl
* Package: com.cxj.minio.service.impl
* Description:
*
* @Author cxj
* @Create 2024/1/6 16:18
* @Version 1.0
*/
@Service
@Slf4j
public class MinioFileServiceImpl implements MinioFileService {
@Autowired
private MinioProperties minioProperties;
@Autowired
private MinioClient minioClient;
@Override
public String uploadFile(String filePath, String targetFolder, String filename) {
FileInputStream fis = null;
//获取存储桶
String bucketName = minioProperties.getBucketName();
try {
fis = new FileInputStream(filePath);
// 检查bucket是否存在
boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
// 如果不存在,那么此时就创建一个新的桶
if (!found) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
} else { // 如果存在打印信息
log.info("Bucket " + bucketName + " already exists.");
}
//设置存放在bucket中的路径
String objectName;
if(targetFolder != null && !targetFolder.equals("")){
objectName = targetFolder + "/" + filename;
}else{
objectName = filename;
}
//上传文件
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.bucket(bucketName)
.stream(fis, fis.available(), -1)
.object(objectName)
.build();
minioClient.putObject(putObjectArgs) ;
return minioProperties.getReadPath() + "/" + bucketName + "/" + objectName;
}catch(Exception e){
log.error("minio put file error.",e);
throw new RuntimeException("上传文件失败");
}finally {
//防止内存泄漏
if (fis != null) {
try {
fis.close(); // 关闭流
} catch (IOException e) {
log.debug("inputStream close IOException:" + e.getMessage());
}
}
}
}
@Override
public String upload(MultipartFile multipartFile, String targetFolder, String filename) {
String bucketName = minioProperties.getBucketName();
InputStream fis = null;
try {
fis = multipartFile.getInputStream();
// 检查bucket是否存在
boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
// 如果不存在,那么此时就创建一个新的桶
if (!found) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
} else { // 如果存在打印信息
log.info("Bucket " + bucketName + " already exists.");
}
//设置存放在bucket中的路径
String objectName;
if(targetFolder != null && !targetFolder.equals("")){
objectName = targetFolder + "/" + filename;
}else{
objectName = filename;
}
//上传文件
PutObjectArgs putObjectArgs = PutObjectArgs.builder()
.bucket(bucketName)
.stream(fis, fis.available(), -1)
.object(objectName)
.build();
minioClient.putObject(putObjectArgs) ;
return minioProperties.getReadPath() + "/" + bucketName + "/" + objectName;
}catch(Exception e){
log.error("minio put file error.",e);
throw new RuntimeException("上传文件失败");
}finally {
//防止内存泄漏
if (fis != null) {
try {
fis.close(); // 关闭流
} catch (IOException e) {
log.debug("inputStream close IOException:" + e.getMessage());
}
}
}
}
测试:
@SpringBootTest(classes = MinioApplication.class)
@RunWith(SpringRunner.class)
public class MinioTest {
@Autowired
private MinioFileService minioFileService;
@Test
public void testUploadFile() {
String filePath = "E:\\video\\video.mp4";
String targetPath = "2024/1/6";
String filename = "testvideo.mp4";
minioFileService.uploadFile(filePath, targetPath, filename);
}
}
3.3.2 删除文件
MinioFileService
/**
* 删除文件
* @param pathUrl 文件全路径
*/
public void delete(String pathUrl);
MinioFileServiceImpl
RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucket).object(filePath).build();
@Service
@Slf4j
public class MinioFileServiceImpl implements MinioFileService {
@Autowired
private MinioProperties minioProperties;
@Autowired
private MinioClient minioClient;
/**
* 删除文件
* @param pathUrl 文件全路径
*/
@Override
public void delete(String pathUrl) {
// http://ip:9090/test-bucket/2024/1/6/testvideo.mp4
String key = pathUrl.replace(minioProperties.getEndpoint()+"/","");
int index = key.indexOf("/");
String bucket = key.substring(0, index);//test-bucket
String filePath = key.substring(index + 1);// 2024/1/6/testvideo.mp4
// 删除Objects
RemoveObjectArgs removeObjectArgs = RemoveObjectArgs.builder().bucket(bucket).object(filePath).build();
try {
minioClient.removeObject(removeObjectArgs);
} catch (Exception e) {
log.error("minio remove file error. pathUrl:{}",pathUrl);
e.printStackTrace();
}
}
}
测试:
@SpringBootTest(classes = MinioApplication.class)
@RunWith(SpringRunner.class)
@Slf4j
public class MinioTest {
@Autowired
private MinioFileService minioFileService;
@Test
public void testDeleteFile() {
String pathUrl = "http://ip:9090/test-bucket/2024/1/6/testvideo.mp4";
minioFileService.delete(pathUrl);
}
}
删除成功
3.3.3 下载文件
MinioFileService
/**
* 获取文件
* @param pathUrl 文件全路径
* @return 文件流
*
*/
public byte[] getFile(String pathUrl);
/**
* 下载文件
* @param pathUrl 文件全路径
* @param response
* @param filename 保存的文件名
* @return 文件流
*
*/
public void downloadFile(String pathUrl, HttpServletResponse response, String filename);
MinioFileServiceImpl
从bucket中获取文件:
根据bucketname以及在bucket存储的url找到文件,返回InputStream
GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(minioProperties.getBucketName()).object(filePath).build();
inputStream = minioClient.getObject(objectArgs);
@Service
@Slf4j
public class MinioFileServiceImpl implements MinioFileService {
@Autowired
private MinioProperties minioProperties;
@Autowired
private MinioClient minioClient;
/**
* 获取文件
* @param pathUrl 文件全路径
* @return 文件流
*
*/
@Override
public byte[] getFile(String pathUrl) {
String key = pathUrl.replace(minioProperties.getEndpoint()+"/","");
int index = key.indexOf("/");
String bucket = key.substring(0, index);
String filePath = key.substring(index + 1);
InputStream inputStream = null;
try {
//找到minio中的指定资源,返回InputStream
GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(minioProperties.getBucketName()).object(filePath).build();
inputStream = minioClient.getObject(objectArgs);
} catch (Exception e) {
log.error("minio down file error. pathUrl:{}",pathUrl);
e.printStackTrace();
}
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
byte[] buff = new byte[100];
int rc = 0;
while (true) {
try {
if (!((rc = inputStream.read(buff, 0, 100)) > 0)) break;
} catch (IOException e) {
e.printStackTrace();
}
byteArrayOutputStream.write(buff, 0, rc);
}
return byteArrayOutputStream.toByteArray();
}
/**
* 下载文件
* @param pathUrl 文件全路径
* @param response
* @param filename 保存的文件名
* @return 文件流
*
*/
@Override
public void downloadFile(String pathUrl, HttpServletResponse response, String filename){
InputStream in = null;
OutputStream out = null;
try{
//从pathUrl中获取bucketName和filePath
String key = pathUrl.replace(minioProperties.getEndpoint()+"/","");
int index = key.indexOf("/");
String bucket = key.substring(0, index);
String filePath = key.substring(index + 1);
//找到minio中的指定资源,返回InputStream
GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(minioProperties.getBucketName()).object(filePath).build();
in = minioClient.getObject(objectArgs);
int length = 0;
byte[] buffer = new byte[1024];
out = response.getOutputStream();
response.reset();
response.addHeader("Content-Disposition",
" attachment;filename=" + URLEncoder.encode(filename, "UTF-8"));
response.setContentType("application/octet-stream");
while ((length = in.read(buffer)) > 0) {
out.write(buffer, 0, length);
}
}catch (Exception e){
e.printStackTrace();
}finally {
if (in != null){
try {
in.close();
} catch (Exception e) {
throw new RuntimeException(e);
}
}
if (out != null) {
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
测试1:获取文件
@SpringBootTest(classes = MinioApplication.class)
@RunWith(SpringRunner.class)
@Slf4j
public class MinioTest {
@Autowired
private MinioFileService minioFileService;
@Test
public void testDownloadFile() {
String pathUrl = "http:/ip:9090/test-bucket/2024/1/6/testvideo.mp4";
byte[] bytes = minioFileService.getFile(pathUrl);
File file = null;
FileOutputStream fos = null;
try {
//将bytes数组转为文件
file = new File("D:\\video.mp4");
//根据文件构建文件输出流
fos = new FileOutputStream(file);
//写byte到文件输出流里
fos.write(bytes, 0, bytes.length);
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
下载成功!
测试2:下载文件
@RestController
@RequestMapping("/minio")
public class MinioController {
@Autowired
private MinioFileService minioFileService;
@RequestMapping("/download")
public void testDownLoadFile(HttpServletResponse response){
String pathUrl = "http://ip:9090/test-bucket/2024/1/6/testvideo.mp4";
minioFileService.downloadFile(pathUrl, response,"myvideo.mp4");
}
}
总结
本文整理了MinIO的相关技术,包含使用docker部署MinIO,对MinIO的配置,以及使用MinIO作为文件服务器进行文件的上传、下载、删除操作。