MinIO【对象存储服务】
一、对象存储的方式对比
存储方式 | 优点 | 缺点 |
服务器磁盘 | 开发便捷,成本低 | 扩展困难 |
分布式文件系统 | 容易实现扩容 | 复杂度高 |
第三方存储 | 开发简单,功能强大,免维护 | 收费 |
二、分布式文件系统
存储方式 | 优点 | 缺点 |
FastDFS |
1、主备服务,高可用 2、支持主从文件,支持自定义扩展名 3、支持动态扩容 |
1、没有完备的官方文档,近几年没有更新 2、环境搭建较为模范 |
MinIO |
1、性能高,准硬件条件下它能达到 55GB/s 的读、35GB/s 的写速率 2、部署自带管理界面 3、MiniIO.Inc 运营的开源项目,社区活跃度高 4、提供了所有主流开发语言的SDK |
1、不支持动态增加节点 |
三、MinIO概述
MinIO 基于 Apache License v2.0 开源协议的对象存储服务,可以作为云存储的解决方案用来保存海量的图片、视频、文档。
Golang语言实现,配置简单,单行命令可以运行起来。
MinIO 兼容亚马逊 S3 云存储服务接口,适合于存储大容量非结构化的数据,一个对象文件可以是任意大小,从 几kb 到 最大 5T 不等。
官方文档:https://www.minio.org.cn/docs/minio/kubernetes/upstream/index.html
四、基本概念
bucker - 类比于文件系统的目录
Object - 类比于文件系统的文件
Keys - 类比文件名
右下角的 “ + ” 号,创建一个桶
五、快速入门
导入依赖
<dependencies> <dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>7.1.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> </dependencies>
启动类
package com.heima.minio; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MinIOApplication { public static void main(String[] args) { SpringApplication.run(MinIOApplication.class,args); } }
测试类
package com.heima.minio.test; import io.minio.MinioClient; import io.minio.PutObjectArgs; import java.io.FileInputStream; import java.io.FileNotFoundException; public class MinIOTest { /** * 把list.html文件上传到minio中,并且可以在浏览器中访问 * @param args */ public static void main(String[] args) { try { FileInputStream fileInputStream = new FileInputStream("C:/VmWare/hmtt/IO/list.html"); // 1.获取minio的链接信息,创建一个minio的客户端 MinioClient minioClient = MinioClient.builder().credentials("minio", "minio123").endpoint("http://192.168.200.130:9000").build(); // 2.上传 PutObjectArgs putObjectArgs = PutObjectArgs.builder() .object("list.html") // 文件名称 .contentType("text/html") // 文件类型 .bucket("leadnews") // 桶名称 与minio管理界面创建的桶一致即可 .stream(fileInputStream, fileInputStream.available(), -1) .build(); minioClient.putObject(putObjectArgs); // 访问路径 需要在minio管理界面给 bucket添加一个 Read and Write 权限 System.out.println("http://192.168.200.130:9000/leadnews/list.html"); } catch (Exception e) { e.printStackTrace(); } } }
设置 Bucket的访问权限
六、封装 MinIO 为 starter
概念图
步骤
6.1 创建模块 heima-file-starter
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>7.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
6.2 配置类 MinIOConfigProperties
package com.heima.file.config; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import java.io.Serializable; @Data @ConfigurationProperties(prefix = "minio") // 文件上传 配置前缀file.oss public class MinIOConfigProperties implements Serializable { private String accessKey; private String secretKey; private String bucket; private String endpoint; private String readPath; }
6.3 MinIOConfig
package com.heima.file.config; import com.heima.file.service.FileStorageService; import io.minio.MinioClient; import lombok.Data; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Data @Configuration @EnableConfigurationProperties({MinIOConfigProperties.class}) //当引入FileStorageService接口时 @ConditionalOnClass(FileStorageService.class) public class MinIOConfig { @Autowired private MinIOConfigProperties minIOConfigProperties; @Bean public MinioClient buildMinioClient(){ return MinioClient .builder() .credentials(minIOConfigProperties.getAccessKey(), minIOConfigProperties.getSecretKey()) .endpoint(minIOConfigProperties.getEndpoint()) .build(); } }
6.4 封装操作 MinIO类
FileStorageService
package com.heima.file.service; import java.io.InputStream; /** * @author itheima */ public interface FileStorageService { /** * 上传图片文件 * @param prefix 文件前缀 * @param filename 文件名 * @param inputStream 文件流 * @return 文件全路径 */ public String uploadImgFile(String prefix, String filename,InputStream inputStream); /** * 上传html文件 * @param prefix 文件前缀 * @param filename 文件名 * @param inputStream 文件流 * @return 文件全路径 */ public String uploadHtmlFile(String prefix, String filename,InputStream inputStream); /** * 删除文件 * @param pathUrl 文件全路径 */ public void delete(String pathUrl); /** * 下载文件 * @param pathUrl 文件全路径 * @return * */ public byte[] downLoadFile(String pathUrl); }
MinIOFileStorageService
package com.heima.file.service.impl; import com.heima.file.config.MinIOConfig; import com.heima.file.config.MinIOConfigProperties; import com.heima.file.service.FileStorageService; import io.minio.GetObjectArgs; import io.minio.MinioClient; import io.minio.PutObjectArgs; import io.minio.RemoveObjectArgs; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Import; import org.springframework.util.StringUtils; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.text.SimpleDateFormat; import java.util.Date; @Slf4j @EnableConfigurationProperties(MinIOConfigProperties.class) @Import(MinIOConfig.class) public class MinIOFileStorageService implements FileStorageService { @Autowired private MinioClient minioClient; @Autowired private MinIOConfigProperties minIOConfigProperties; private final static String separator = "/"; /** * @param dirPath * @param filename yyyy/mm/dd/file.jpg * @return */ public String builderFilePath(String dirPath,String filename) { StringBuilder stringBuilder = new StringBuilder(50); if(!StringUtils.isEmpty(dirPath)){ stringBuilder.append(dirPath).append(separator); } SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd"); String todayStr = sdf.format(new Date()); stringBuilder.append(todayStr).append(separator); stringBuilder.append(filename); return stringBuilder.toString(); } /** * 上传图片文件 * @param prefix 文件前缀 * @param filename 文件名 * @param inputStream 文件流 * @return 文件全路径 */ @Override public String uploadImgFile(String prefix, String filename,InputStream inputStream) { String filePath = builderFilePath(prefix, filename); try { PutObjectArgs putObjectArgs = PutObjectArgs.builder() .object(filePath) .contentType("image/jpg") .bucket(minIOConfigProperties.getBucket()).stream(inputStream,inputStream.available(),-1) .build(); minioClient.putObject(putObjectArgs); StringBuilder urlPath = new StringBuilder(minIOConfigProperties.getReadPath()); urlPath.append(separator+minIOConfigProperties.getBucket()); urlPath.append(separator); urlPath.append(filePath); return urlPath.toString(); }catch (Exception ex){ log.error("minio put file error.",ex); throw new RuntimeException("上传文件失败"); } } /** * 上传html文件 * @param prefix 文件前缀 * @param filename 文件名 * @param inputStream 文件流 * @return 文件全路径 */ @Override public String uploadHtmlFile(String prefix, String filename,InputStream inputStream) { String filePath = builderFilePath(prefix, filename); try { PutObjectArgs putObjectArgs = PutObjectArgs.builder() .object(filePath) .contentType("text/html") .bucket(minIOConfigProperties.getBucket()).stream(inputStream,inputStream.available(),-1) .build(); minioClient.putObject(putObjectArgs); StringBuilder urlPath = new StringBuilder(minIOConfigProperties.getReadPath()); urlPath.append(separator+minIOConfigProperties.getBucket()); urlPath.append(separator); urlPath.append(filePath); return urlPath.toString(); }catch (Exception ex){ log.error("minio put file error.",ex); ex.printStackTrace(); throw new RuntimeException("上传文件失败"); } } /** * 删除文件 * @param pathUrl 文件全路径 */ @Override public void delete(String pathUrl) { String key = pathUrl.replace(minIOConfigProperties.getEndpoint()+"/",""); int index = key.indexOf(separator); String bucket = key.substring(0,index); String filePath = key.substring(index+1); // 删除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(); } } /** * 下载文件 * @param pathUrl 文件全路径 * @return 文件流 * */ @Override public byte[] downLoadFile(String pathUrl) { String key = pathUrl.replace(minIOConfigProperties.getEndpoint()+"/",""); int index = key.indexOf(separator); String bucket = key.substring(0,index); String filePath = key.substring(index+1); InputStream inputStream = null; try { inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(minIOConfigProperties.getBucket()).object(filePath).build()); } 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(); } }
6.5 对外加入自动配置 在 resources 中新建 META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.heima.file.service.impl.MinIOFileStorageService
七、其他微服务使用
7.1 导入 heima-file-starter 的依赖
<dependency> <groupId>com.heima</groupId> <artifactId>heima-file-starter</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
7.2 在微服务中添加minio所需要的配置
minio: accessKey: minio secretKey: minio123 bucket: leadnews endpoint: http://192.168.200.130:9000 readPath: http://192.168.200.130:9000
7.3 在对应使用的业务类中注入FileStorageService
package com.heima.minio.test; import com.heima.file.service.FileStorageService; import com.heima.minio.MinioApplication; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import java.io.FileInputStream; import java.io.FileNotFoundException; @SpringBootTest(classes = MinioApplication.class) @RunWith(SpringRunner.class) public class MinioTest { @Autowired private FileStorageService fileStorageService; @Test public void testUpdateImgFile() { try { FileInputStream fileInputStream = new FileInputStream("E:\\tmp\\ak47.jpg"); String filePath = fileStorageService.uploadImgFile("", "ak47.jpg", fileInputStream); System.out.println(filePath); } catch (FileNotFoundException e) { e.printStackTrace(); } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧