Minio 是什么?
Minio是一个开源的、云原生的分布式对象存储系统,是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据。
它一大特点就是轻量,虽然轻量,却拥有着不错的性能。使用简单,功能强大,支持各种平台,单个文件最大5TB,兼容 Amazon S3接口,提供了 Java、Python、GO等多版本SDK支持。
官网:https://www.minio.org.cn
1. 那 MINIO 是对象存储,和 COS 这些有什么区别呢?
MinIO 和 COS(腾讯云对象存储)都是云存储服务,但它们有一些区别。
-
提供商:
-
MinIO 是一个开源的对象存储服务器,可以在本地或者私有云环境中部署和运行。 -
COS 是由腾讯云提供的对象存储服务,部署在腾讯云的基础设施上。 -
定位:
-
MinIO 更加注重于提供给开发者一个简单、高性能的对象存储解决方案,特别适合在本地或者私有云环境中构建大规模的数据存储系统。 -
COS 则是一个公共云服务,适用于各种规模的企业和个人用户,提供了稳定、可靠、高可用的对象存储服务。 -
功能特性:
-
MinIO 提供了丰富的功能,包括高性能、S3 兼容性、分布式存储、数据加密、版本控制等。 -
COS 也提供了类似的功能,同时还有一些与腾讯云其他服务集成的特性,比如 CDN 加速、数据迁移等。 -
适用场景:
-
如果你需要在本地或者私有云环境中构建高性能、可扩展的对象存储系统,那么 MinIO 是一个很好的选择。 -
如果你需要在公共云环境中部署应用,或者需要与其他腾讯云服务集成,那么 COS 是一个更合适的选择。
如果你需要自己控制基础设施并且注重性能和灵活性,可以考虑使用 MinIO;
如果你需要一个稳定、可靠的公共云对象存储服务,或者需要与腾讯云其他服务集成,那么 COS 是一个不错的选择。
Windows 环境安装 MINIO:
1. 下载服务端和客户端安装包文件
下载地址:https://min.io/download#/agpl-kubernetes
服务端文件:minio.exe 用于接收文件信息
客户端文件:mac.exe 用于上传文件,如果用程序代码操作文件存储,只启动服务端就 ok。
2. 创建 MINIO 目录(不强求、但推荐这样做)
1、创建一个 minio 的文件夹,存放 minio 相关文件;
2、在 minio 文件夹中创建 minio 的应用目录 bin,存放相关数据文件;
3、在 minio 文件夹中创建 minio 的数据目录 data,存放相关数据文件;
4、在 minio 文件夹中创建 minio 的数据目录 log,存储相关日志;
3. 启动 minio 服务器
启动方式有两种,个人比较推荐第一种方式:
1、命令启动
2、新建 .bat 脚本文件启动
特别提示:在 Windows 安装软件我们都习惯双击 .exe 文件启动,minio 中可不行,可能会导致服务最终启动失败;所以无论是 windows 还是 Linux 都建议通过命令启动。
命令启动:
以管理员权限打开 cmd 窗口,进入到 minio.exe 所在 bin 目录
minio.exe server D:minIO
或者
.minio.exe server D:develpominiodata --console-address "127.0.0.1:9000" --address "127.0.0.1:9005"
cmd 进入到 minio 的 bin 目录下,执行下述操作:
setx MINIO_ROOT_USER name // 设置登录账号
setx MINIO_ROOT_PASSWORD password // 设置登录密码
4. 访问 minio 服务器
http://127.0.0.1:9000/ 输入用户密码即可。
简单介绍一下可视化界面:
使用 Spring 整合 MINIO
说了这么多,到底应该怎么使用 Spring 去操作 MINIO 呢?
1. 创建 Spring 工程并引入依赖
<!-- MINIO 对象存储 -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.3.5</version>
</dependency>
<!-- okhttp3(MINIO 对象存储 依赖) -->
<dependency>
<groupId>com.squareup.okhttp3</groupId>
<artifactId>okhttp</artifactId>
<version>4.8.1</version>
</dependency>
2. 配置上传信息
minio:
bucket-name: test-bucket
endpoint: http://192.168.31.18:9000
access-key: accessKey
secret-key: secretKey
accessKey 和 secretKey 生成方法在上面有强调过。
3. 编写 minio 属性值
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* minio 属性值
*/
@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {
/** 连接url */
@Value("${minio.endpoint}")
private String endpoint;
/** 公钥 */
@Value("${minio.access-key}")
private String accessKey;
/** 私钥 */
@Value("${minio.secret-key}")
private String secretKey;
/** 桶名 */
@Value("${minio.bucket-name}")
private String bucketName;
}
4. 编写 minio 核心配置类
import com.example.projectBudget.common.entity.MinioProperties;
import io.minio.MinioClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* minio核心配置类
* 通过注入 MinIO 服务器的相关配置信息,得到 MinioClient 对象,我们上传文件依赖此对象
*/
@Configuration
public class MinioConfig {
@Autowired
private MinioProperties prop;
/**
* 获取 MinioClient
*
* @return MinioClient
*/
@Bean
public MinioClient minioClient() {
return MinioClient.builder().endpoint(prop.getEndpoint()).credentials(prop.getAccessKey(), prop.getSecretKey())
.build();
}
}
5. 编写 minio 工具类
import com.alibaba.excel.util.CollectionUtils;
import com.example.projectBudget.common.entity.MinioProperties;
import com.example.projectBudget.common.exceptions.MyException;
import io.micrometer.core.instrument.util.StringUtils;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Slf4j
@Component
public class MinioUtils {
@Autowired
private MinioClient minioClient;
@Autowired
private MinioProperties minioProperties;
/**
* 创建桶
*
* @param bucketName 桶名称
*/
@SneakyThrows
public void createBucket(String bucketName) {
boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (!found) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).region("cn-beijing").build());
}
}
/**
* 删除桶
*
* @param bucketName 桶名称
*/
@SneakyThrows
public void removeBucket(String bucketName) {
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
}
/**
* 上传文件
*
* @param fileName 文件名
* @param stream 流
* @param fileSize 文件大小
* @param type 文件类型
* @throws Exception
*/
public void uploadFile(String bucketName, String fileName, InputStream stream, Long fileSize, String type)
throws Exception {
minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(stream, fileSize, -1)
.contentType(type).build());
}
/**
* 下载文件
*
* @param bucketName 存储桶名称
* @param fileName 文件名
* @param outputStream 输出流
* @throws Exception
*/
public void downloadFile(String bucketName, String fileName, OutputStream outputStream)
throws Exception {
// 获取对象并将其写入输出流
try (InputStream inputStream = minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.build())) {
byte[] buf = new byte[16384];
int bytesRead;
while ((bytesRead = inputStream.read(buf, 0, buf.length)) >= 0) {
outputStream.write(buf, 0, bytesRead);
}
}
}
/**
* 判断文件夹是否存在
*
* @param bucketName 桶名称
* @param prefix 文件夹名字
* @return
*/
@SneakyThrows
public Boolean folderExists(String bucketName, String prefix) {
Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix(
prefix).recursive(false).build());
for (Result<Item> result : results) {
Item item = result.get();
if (item.isDir()) {
return true;
}
}
return false;
}
/**
* 创建文件夹
*
* @param bucketName 桶名称
* @param path 路径
*/
@SneakyThrows
public void createFolder(String bucketName, String path) {
minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(path)
.stream(new ByteArrayInputStream(new byte[]{}), 0, -1).build());
}
/**
* 获取文件在minio在服务器上的外链
*
* @param bucketName 桶名称
* @param objectName 文件名
* @return 可访问的URL
* expiry(int duration, TimeUnit unit) 设置有效时间,这里是设置为URL的有效期为1天,即在一天以后国企.
*/
@SneakyThrows
public String getObjectUrl(String bucketName, String objectName) {
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(
bucketName).object(objectName).expiry(1, TimeUnit.DAYS).build());
}
/**
* 生成可访问的url
*
* @param filePath 文件路径
* @return 可访问的文件, 过期时间1day
* @author HuangLongFei
* @since 2023/3/11
*/
public String generateUrl(String filePath) {
return getObjectUrl(minioProperties.getBucketName(), filePath);
}
/**
* 批量删除文件
*
* @param filePathList 文件路径列表
* @author HuangLongFei
* @since 2023/7/17
*/
public void deleteFile(List<String> filePathList) throws MyException {
if (CollectionUtils.isEmpty(filePathList)) {
return;
}
// 构建要删除的文件列表
List<DeleteObject> deleteFileList = new ArrayList<>();
for (String filePath : filePathList) {
deleteFileList.add(new DeleteObject(filePath));
}
try {
// 批量删除文件
RemoveObjectsArgs fileArgs = RemoveObjectsArgs.builder().bucket(minioProperties.getBucketName()).objects(
deleteFileList).build();
Iterable<Result<DeleteError>> fileDeleteResult = minioClient.removeObjects(fileArgs);
// 注意这里必须迭代返回结果,因为removeObjects是惰性的
for (Result<DeleteError> result : fileDeleteResult) {
DeleteError deleteError = result.get();
}
} catch (Exception e) {
throw new MyException("MINIO_CALL_ERROR");
}
}
/**
* 获取minio中,某个bucket中所有的文件名
*/
public List<String> getFileList(String bucketName) {
// 根据bucketName,获取该桶下所有的文件信息
Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName)
.recursive(true).build());
List<String> fileNameList = new ArrayList<>();
for (Result<Item> result : results) {
Item item;
String fileName = null;
try {
item = result.get();
fileName = item.objectName();
fileNameList.add(fileName);
} catch (Exception e) {
log.error("call the minioClient.listObjects Error. result={} and message = {}", result, e);
}
// 获取文件的访问路径
if (StringUtils.isNotBlank(fileName)) {
String accessUrl = getObjectUrl(bucketName, fileName);
}
}
return fileNameList;
}
6. 编写测试方法
@Autowired
private MinioUtils minioUtils;
@Autowired
private MinioProperties minioProperties;
private final static String BUCKET_NAME_TEST = "test-bucket";
/***
* 测试目的:创建一个bucket
*/
@Test
public void createBucket() {
String bucketName = BUCKET_NAME_TEST;
minioUtils.createBucket(bucketName);
System.out.println("create " + bucketName + " success!");
}
/***
* 测试目的:删除一个bucket
*/
@Test
public void removeBucket() {
String bucketName = BUCKET_NAME_TEST;
minioUtils.removeBucket(bucketName);
System.out.println("remove " + bucketName + " success!");
}
/***
* 测试目的:创建文件夹,并上传路径
*/
@Test
public void uploadFile() {
String filePath = "C:/Users/ADMIN/Desktop/aaa.pdf";
File file = new File(filePath);
// 设置要上传的路径,例如 /test/2023/123.jpg
filePath = "/test/2023/" + file.getName();
try {
FileInputStream fileInputStream = new FileInputStream(file);
minioUtils.uploadFile(BUCKET_NAME_TEST, filePath, fileInputStream, file.length(), "application/pdf");
System.out.println("上传成功!,文件路径:" + filePath);
} catch (Exception e) {
System.out.println("上传失败");
e.printStackTrace();
}
}
/**
* 测试目的: 下载文件
*/
@Test
public void downloadFileTest() {
// 设置下载文件的存储桶和路径
String bucketName = "test-bucket"; // 替换为您的存储桶名称
String filePath = "/test/2023/aaa.pdf"; // 替换为您上传的文件路径
// 设置下载文件的本地保存路径
String localFilePath = "C:/Users/ADMIN/Desktop/downloaded_file.pdf"; // 替换为您希望保存的本地路径
try {
// 创建输出流
OutputStream outputStream = new FileOutputStream(localFilePath);
// 下载文件
minioUtils.downloadFile(bucketName, filePath, outputStream);
// 关闭输出流
outputStream.close();
// 检查文件是否成功下载,假设文件存在且非空
assertTrue("下载文件失败", new File(localFilePath).exists());
System.out.println("下载成功!, 文件保存路径:" + localFilePath);
} catch (Exception e) {
System.out.println("下载失败");
e.printStackTrace();
}
}
/***
* 测试目的:生成可访问的图片的URL
*/
@Test
public void getObjectUrl() {
String filePath = "D:/app/webfile/598889851944697856/pdf.png";
String url = minioUtils.getObjectUrl(BUCKET_NAME_TEST, filePath);
System.out.println(url);
}
到这里 Spring 整合 MINIO 对象存储实现基本功能就完成啦~ 上传之后就可以根据桶名以及文件在桶中的路径进行在线访问了,比如:http://127.0.0.1:9000/test-bucket/test/2023/helloworld.jpg
但是要在在线访问之前先要设置存储桶的状态为公开:
原文始发于微信公众号(Coder香):开源利器:打造私有云存储解决方案的首选——MinIO 详解
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/276403.html