SpringBoot - Minio
1、简介
MinIO 是一个开源的对象存储服务器,用于存储和管理大规模的非结构化数据,例如图像、视频、日志文件、备份和容器镜像。MinIO 旨在提供高性能、高可用性、可扩展性和易用性的对象存储解决方案,适用于私有云、公共云和混合云环境。
2、引入依赖
<dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.8.1</version> <!-- minio 依赖于 okhttp 且版本较高。注意,spring-boot-dependencies 中的不够高 -->
</dependency>
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.3.9</version>
</dependency>
3、相关配置
# minio 文件存储配置信息
minio:endpoint: http://192.168.128.128:9000accesskey: minioadminsecretKey: minioadminbucketName: test
4、配置类和配置属性
@Configuration
@Component
@ConfigurationProperties(prefix = "minio")
@Data
public class MinioConfig {private String endpoint;private String accessKey;private String secretKey;private String bucketName;@Beanpublic MinioClient getMinioClient() throws InvalidPortException {MinioClient minioClient = MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();return minioClient;}
}
5、创建一个数据表,用于保存上传到minio的文件的信息
CREATE TABLE `minio_file` (`id` bigint(20) NOT NULL COMMENT '文件id',`original_file_name` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '原始文件名称',`file_ext_name` varchar(15) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '文件拓展名',`file_size` bigint(20) DEFAULT NULL COMMENT '文件大小(单位:字节)',`file_name` varchar(35) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '存入minio时的文件名称',`mime` varchar(50) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '文件的content-type',`file_url` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '文件路径',`is_delete` tinyint(1) DEFAULT NULL COMMENT '是否删除 0 否 1 是',`create_by` varchar(25) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '创建者',`update_by` varchar(25) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '更改者',`create_time` datetime DEFAULT NULL COMMENT '创建时间',`update_time` datetime DEFAULT NULL COMMENT '更新时间',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
6、 MinIO 客户端工具类
@Component
@Slf4j
public class MinioClientUtils {@Autowiredprivate MinioClient minioClient;private static final int DEFAULT_EXPIRY_TIME = 7 * 24 * 3600;/*** 检查存储桶是否存在** @param bucketName 存储桶名称*/public boolean bucketExists(String bucketName) throws Exception{boolean flag = false;flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());if (flag) {return true;}return false;}/*** 创建存储桶** @param bucketName 存储桶名称*/public boolean makeBucket(String bucketName) throws Exception {boolean flag = bucketExists(bucketName);if (!flag) {minioClient.makeBucket( MakeBucketArgs.builder().bucket(bucketName).build());return true;} else {return false;}}/*** 列出所有存储桶*/public List<Bucket> listBuckets() throws Exception {return minioClient.listBuckets();}/*** 删除存储桶** @param bucketName 存储桶名称*/public boolean removeBucket(String bucketName) throws Exception {boolean flag = bucketExists(bucketName);if (flag) {Iterable<Result<Item>> myObjects = listObjects(bucketName);for (Result<Item> result : myObjects) {Item item = result.get();// 有对象文件,则删除失败if (item.size() > 0) {return false;}}// 删除存储桶,注意,只有存储桶为空时才能删除成功。minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());flag = bucketExists(bucketName);if (!flag) {return true;}}return false;}/*** 列出存储桶中的所有对象** @param bucketName 存储桶名称*/public Iterable<Result<Item>> listObjects(String bucketName) throws Exception {boolean flag = bucketExists(bucketName);if (flag) {return minioClient.listObjects( ListObjectsArgs.builder().bucket(bucketName).build());}return null;}/*** 列出存储桶中的所有对象名称** @param bucketName 存储桶名称*/public List<String> listObjectNames(String bucketName) throws Exception {List<String> listObjectNames = new ArrayList<>();boolean flag = bucketExists(bucketName);if (flag) {Iterable<Result<Item>> myObjects = listObjects(bucketName);for (Result<Item> result : myObjects) {Item item = result.get();listObjectNames.add(item.objectName());}}return listObjectNames;}/*** 通过文件上传到对象** @param bucketName 存储桶名称* @param objectName 存储桶里的对象名称* @param fileName File name*/public boolean uploadObject(String bucketName, String objectName, String fileName) throws Exception {boolean flag = bucketExists(bucketName);if (flag) {minioClient.uploadObject( UploadObjectArgs.builder().bucket(bucketName).object(objectName).filename(fileName).build());ObjectStat statObject = statObject(bucketName, objectName);if (statObject != null && statObject.length() > 0) {return true;}}return false;}/*** 文件上传** @param bucketName 存储捅名称* @param multipartFile 文件* @param filename 文件名*/public void putObject(String bucketName, MultipartFile multipartFile, String filename) throws Exception {PutObjectOptions putObjectOptions = new PutObjectOptions(multipartFile.getSize(), PutObjectOptions.MIN_MULTIPART_SIZE);putObjectOptions.setContentType(multipartFile.getContentType());minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(filename).stream(
multipartFile.getInputStream(),multipartFile.getSize(), -1).contentType(multipartFile.getContentType()
).build()
);}/*** 通过InputStream上传对象** @param bucketName 存储桶名称* @param objectName 存储桶里的对象名称* @param inputStream 要上传的流* @param contentType 上传的文件类型 例如 video/mp4 image/jpg*/public boolean putObject(String bucketName, String objectName, InputStream inputStream,String contentType) throws Exception {boolean flag = bucketExists(bucketName);if (flag) {minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(//不清楚文件的大小时,可以传-1,10485760。如果知道大小也可以传入size,partsize。inputStream, -1, 10485760).contentType(contentType).build()
);ObjectStat statObject = statObject(bucketName, objectName);if (statObject != null && statObject.length() > 0) {return true;}}return false;}/*** 以流的形式获取一个文件对象** @param bucketName 存储桶名称* @param objectName 存储桶里的对象名称*/public InputStream getObject(String bucketName, String objectName) throws Exception {boolean flag = bucketExists(bucketName);if (flag) {ObjectStat statObject = statObject(bucketName, objectName);if (statObject != null && statObject.length() > 0) {InputStream stream = minioClient.getObject( GetObjectArgs.builder().bucket(bucketName).object(objectName).build());return stream;}}return null;}/*** 以流的形式获取一个文件对象(断点下载)** @param bucketName 存储桶名称* @param objectName 存储桶里的对象名称* @param offset 起始字节的位置* @param length 要读取的长度 (可选,如果无值则代表读到文件结尾)*/public InputStream getObject(String bucketName, String objectName, long offset, Long length) throws Exception {boolean flag = bucketExists(bucketName);if (flag) {ObjectStat statObject = statObject(bucketName, objectName);if (statObject != null && statObject.length() > 0) {InputStream stream = minioClient.getObject(
GetObjectArgs.builder().bucket(bucketName).object(objectName).offset(1024L).length(4096L).build()
);return stream;}}return null;}/*** 下载并将文件保存到本地** @param bucketName 存储桶名称* @param objectName 存储桶里的对象名称* @param fileName File name*/public boolean downloadObject(String bucketName, String objectName, String fileName) throws Exception {boolean flag = bucketExists(bucketName);if (flag) {ObjectStat statObject = statObject(bucketName, objectName);if (statObject != null && statObject.length() > 0) {minioClient.downloadObject(DownloadObjectArgs.builder().bucket(bucketName).object(objectName).filename(fileName).build());return true;}}return false;}/*** 删除一个对象** @param bucketName 存储桶名称* @param objectName 存储桶里的对象名称*/public boolean removeObject(String bucketName, String objectName) throws Exception {boolean flag = bucketExists(bucketName);if (flag) {minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());return true;}return false;}/*** 删除指定桶的多个文件对象,返回删除错误的对象列表,全部删除成功,返回空列表** @param bucketName 存储桶名称* @param objectNames 含有要删除的多个object名称的迭代器对象* eg:* List<DeleteObject> objects = new LinkedList<>();* objects.add(new DeleteObject("my-objectname1"));* objects.add(new DeleteObject("my-objectname2"));* objects.add(new DeleteObject("my-objectname3"));*/public List<String> removeObjects(String bucketName, List<DeleteObject> objectNames) throws Exception {List<String> deleteErrorNames = new ArrayList<>();boolean flag = bucketExists(bucketName);if (flag) {Iterable<Result<DeleteError>> results = minioClient.removeObjects(
RemoveObjectsArgs.builder().bucket(bucketName).objects(objectNames).build()
);for (Result<DeleteError> result : results) {DeleteError error = result.get();deleteErrorNames.add(error.objectName());}}return deleteErrorNames;}/*** 生成一个给HTTP GET请求用的presigned URL。* 浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。** @param bucketName 存储桶名称* @param objectName 存储桶里的对象名称* @param expires 失效时间(以秒为单位),默认是7天,不得大于七天*/public String getPresignedObjectUrl(String bucketName, String objectName, Integer expires) throws Exception {boolean flag = bucketExists(bucketName);String url = "";if (flag) {if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {throw new InvalidExpiresRangeException(expires, "expires must be in range of 1 to " + DEFAULT_EXPIRY_TIME);}try {url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(bucketName).object(objectName).expiry(expires)//动态参数
// .expiry(24 * 60 * 60)//用秒来计算一天时间有效期
// .expiry(1, TimeUnit.DAYS)//按天传参
// .expiry(1, TimeUnit.HOURS)//按小时传参数.build());} catch (Exception e) {e.printStackTrace();}}return url;}/*** 生成一个给HTTP PUT请求用的presigned URL。* 浏览器/移动端的客户端可以用这个URL进行上传,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。** @param bucketName 存储桶名称* @param objectName 存储桶里的对象名称* @param expires 失效时间(以秒为单位),默认是7天,不得大于七天*/public String presignedPutObject(String bucketName, String objectName, Integer expires) throws Exception {boolean flag = bucketExists(bucketName);String url = "";if (flag) {if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {try {throw new InvalidExpiresRangeException(expires, "expires must be in range of 1 to " + DEFAULT_EXPIRY_TIME);} catch (InvalidExpiresRangeException e) {e.printStackTrace();}}try {url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.PUT).bucket(bucketName).object(objectName).expiry(expires)//动态参数
// .expiry(24 * 60 * 60)//用秒来计算一天时间有效期
// .expiry(1, TimeUnit.DAYS)//按天传参
// .expiry(1, TimeUnit.HOURS)//按小时传参数.build());} catch (Exception e) {log.error("XmlParserException",e);}}return url;}/*** 获取对象的元数据** @param bucketName 存储桶名称* @param objectName 存储桶里的对象名称*/public ObjectStat statObject(String bucketName, String objectName) throws Exception {boolean flag = bucketExists(bucketName);if (flag) {ObjectStat statObject = null;try {statObject = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());} catch (Exception e) {log.error("XmlParserException",e);}return statObject;}return null;}/*** 文件访问路径** @param bucketName 存储桶名称* @param objectName 存储桶里的对象名称*/public String getObjectUrl(String bucketName, String objectName) throws Exception {boolean flag = bucketExists(bucketName);String url = "";if (flag) {try {url = minioClient.getObjectUrl(bucketName, objectName);} catch (Exception e) {log.error("XmlParserException",e);}}return url;}public void downloadFile(String bucketName, String fileName, String originalName, HttpServletResponse response) {try {InputStream file = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());String filename = new String(fileName.getBytes("ISO8859-1"), StandardCharsets.UTF_8);if (StringUtils.isNotEmpty(originalName)) {fileName = originalName;}response.setHeader("Content-Disposition", "attachment;filename=" + filename);ServletOutputStream servletOutputStream = response.getOutputStream();int len;byte[] buffer = new byte[1024];while ((len = file.read(buffer)) > 0) {servletOutputStream.write(buffer, 0, len);}servletOutputStream.flush();file.close();servletOutputStream.close();} catch (Exception e) {log.error("Exception",e);}}
}
相关文章:
SpringBoot - Minio
1、简介 MinIO 是一个开源的对象存储服务器,用于存储和管理大规模的非结构化数据,例如图像、视频、日志文件、备份和容器镜像。MinIO 旨在提供高性能、高可用性、可扩展性和易用性的对象存储解决方案,适用于私有云、公共云和混合云环境。2、…...
Android --- SystemUI启动流程
1.main 函数入口,调用SystemServer().run()方法 代码路径:frameworks/base/services/java/com/android/server/SystemServer.java 2.run 方法中有3种服务的启动,我们主要看StartOtherService 代码路径:frameworks/base/services/java/com/android/se…...
docker镜像被覆盖了怎么办?通过sha256重新上传镜像
如果一个镜像通过相同的标签被重新推送(覆盖),那么旧的镜像内容虽然在 Docker 的存储中可能仍然存在,但通过原来的标签将无法直接访问到它。Docker 和 Harbor 默认情况下不会自动删除旧的镜像层,除非进行了垃圾回收&am…...
(二十六)Java观察者模式在Android开发中的应用详解
Java观察者模式在Android开发中的应用 观察者模式(Observer Pattern)是一种行为型设计模式,它定义了一种一对多的依赖关系,使得多个观察者对象可以同时监听一个主题对象。当主题对象的状态发生变化时,所有注册的观察者…...
【SpringMVC】深入解析自定义拦截器、注册配置拦截器、拦截路径方法及常见拦截路径、排除拦截路径、拦截器的执行流程
拦截器 上个章节我们完成了强制登录的功能, 后端程序根据Session来判断用户是否登录, 但是实现方法是比较麻烦的: 需要修改每个接口的处理逻辑需要修改每个接口的返回结果接口定义修改, 前端代码也需要跟着修改 有没有更简单的办法, 统一拦截所有的请求, 并进行Se…...
基于VS Code 为核心平台的python语言智能体开发平台搭建
以下是基于 VS Code 为核心平台,整合 Node-RED、Gradio、Docker Desktop 的智能体可视化开发平台优化方案,聚焦工具链深度集成与开发效率提升: 一、核心架构设计 #mermaid-svg-f8l9kYPAlJ2TlpGF {font-family:"trebuchet ms",verd…...
使用最新threejs复刻经典贪吃蛇游戏的3D版,附完整源码
基类Entity 建立基类Entity,实现投影能力、动画入场效果(从小变大的弹性动画)、计算自己在地图格位置的方法。 // 导入gsap动画库(用于创建补间动画) import gsap from gsap// 定义Entity基类 export default class …...
论坛测试报告
作者前言 🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂 🎂 作者介绍: 🎂🎂 🎂 🎉🎉🎉…...
IPMI 与 Redfish API简介
--- ### **IPMI 与 Redfish API 详解** #### **1. IPMI(智能平台管理接口)** **简介** IPMI(Intelligent Platform Management Interface)是一种硬件级别的带外管理标准,允许管理员通过独立于操作系统的网络通道(BMC)监控和管理服务器硬件,即使主机已关机或操作系…...
zset.
zset 有序集合 zset 保留了 set 不能有重复元素的特点 zset 中的每个元素都有一个唯一的浮点类型的分数(score)与之关联,使得 zset 内部的元素是可以维护有序性的。但是这个有序不是用下标作为排序依据的,而是根据分数…...
Windows 部署 DeepSeek 详细教程
一、准备工作 系统要求: 建议Windows 10 22H2 或更高版本,家庭版或专业版上网环境: 建议科学上网,国内访问部分网站会很慢设备要求: 内存8G以上、关闭防火墙 二、安装Ollama 官网链接: https://ollama.com/downloadg…...
过去十年前端框架演变与技术驱动因素剖析
一、技术演进脉络(2013-2023) 2013-2015:结构化需求催生框架雏形 早期的jQuery虽然解决了跨浏览器兼容性问题(如IE8兼容性处理),但其松散的代码组织方式难以支撑复杂应用开发。Backbone.js的出现首次引入M…...
从零开始学A2A一:A2A 协议的高级应用与优化
A2A 协议的高级应用与优化 学习目标 掌握 A2A 高级功能 理解多用户支持机制掌握长期任务管理方法学习服务性能优化技巧 理解与 MCP 的差异 分析多智能体场景下的优势掌握不同场景的选择策略 第一部分:多用户支持机制 1. 用户隔离架构 #mermaid-svg-Awx5UVYtqOF…...
#Linux动态大小裁剪以及包大小变大排查思路
1 动态库裁剪 库分为动态库和静态库,动态库是在程序运行时才加载,静态库是在编译时就加载到程序中。动态库的大小通常比静态库小,因为动态库只包含了程序需要的函数和数据,而静态库则包含了所有的函数和数据。静态库可以理解为引入…...
基于微信小程序的中医小妙招系统的设计与实现
hello hello~ ,这里是 code袁~💖💖 ,欢迎大家点赞🥳🥳关注💥💥收藏🌹🌹🌹 🦁作者简介:一名喜欢分享和记录学习的在校大学生…...
sqlite3的API以及命令行
sqlite是目前最流行的嵌入式数据库。 所谓嵌入式,就是足够简单,可以嵌入到我们自己开发的应用程序之中。 在Linux系统中,sqlite的使用只需要使用它的API,连接它的动态连接库,甚至都不用连接,sqlite的实现…...
css button 点击效果
<!DOCTYPE html> <html lang"zh-CN"><head><meta charset"UTF-8"><title>button点击效果</title><style>#container {display: flex;align-items: center;justify-content: center;}.pushable {position: relat…...
表征流体作用力的参数及其特性
在圆柱绕流研究中,这些参数分别表征流体作用力的关键特性,以下是详细解析: 📊 参数物理意义及工程应用 符号名称物理意义典型值范围(参考)工程意义 C d m a x C_{dmax} Cdmax最大阻力系数瞬时阻力系数&a…...
Foundation Agent:深度赋能AI4DATA
2025年5月17日,第76期DataFunSummit:AI Agent技术与应用峰会将在DataFun线上社区举办。Manus的爆火并非偶然,随着基础模型效果不断的提升,Agent作为大模型的超级应用备受全世界的关注。为了推动其技术和应用,本次峰会计…...
Docker--Docker镜像原理
docker 是操作系统层的虚拟化,所以 docker 镜像的本质是在模拟操作系统。 联合文件系统(UnionFS) 联合文件系统(UnionFS) 是Docker镜像实现分层存储的核心技术,它通过将多个只读层(Image Laye…...
SpringAI+DeepSeek大模型应用开发——2 大模型应用开发架构
目录 2.大模型开发 2.1 模型部署 2.1.1 云服务-开放大模型API 2.1.2 本地部署 搜索模型 运行大模型 2.2 调用大模型 接口说明 提示词角色 编辑 会话记忆问题 2.3 大模型应用开发架构 2.3.1 技术架构 纯Prompt模式 FunctionCalling RAG检索增强 Fine-tuning …...
Transformer 架构 - 编码器 (Transformer Architecture - Encoder)
1.Transformer 编码器整体结构 Transformer 编码器的结构相对直观:它由 N 个完全相同的编码器层 (Encoder Layer) 堆叠而成。 图1: Transformer 编码器整体结构示意图 (简化) 输入序列(例如,通过 embedding 层转换后的词向量)首先会加上位置编码,然后传入第一个编码器层…...
2.2/Q2,Charls最新文章解读
文章题目:Association of uric acid to high-density lipoprotein cholesterol ratio with the presence or absence of hypertensive kidney function: results from the China Health and Retirement Longitudinal Study (CHARLS) DOI:10.1186/s12882-…...
下拉框select标签类型
在我们很多页面里有下拉框的选择,这种元素怎么定位呢?下拉框分为两种类型:我们分别针对这两种元素进行定位和操作 select标签 : 通过select类处理。 非select标签 1、针对下拉框元素,如果是Select标签类型,…...
CentOS 7 linux系统从无到有部署项目
环境部署操作手册 一、Maven安装与配置 1. 下载与解压 下载地址:https://maven.apache.org/download.cgi?spm5238cd80.38b417da.0.0.d54c32cbnOpQh2&filedownload.cgi上传并解压解压命令: tar -zxvf apache-maven-3.9.9-bin.tar.gz -C /usr/loc…...
李飞飞团队新作WorldScore:“世界生成”能力迎来统一评测,3D/4D/视频模型同台PK
从古老神话中对世界起源的幻想,到如今科学家们在实验室里对虚拟世界的构建,人类探索世界生成奥秘的脚步从未停歇。如今,随着人工智能和计算机图形学的深度融合,我们已站在一个全新的起点,能够以前所未有的精度和效率去…...
如何在米尔-STM32MP257开发板上部署环境监测系统
本文将介绍基于米尔电子MYD-LD25X开发板(米尔基于STM35MP257开发板)的环境监测系统方案测试。 摘自优秀创作者-lugl4313820 一、前言 环境监测是当前很多场景需要的项目,刚好我正在论坛参与的一个项目:Thingy:91X 蜂窝物联网原型…...
MySQL之SQL优化
目录 1.插入数据 2.大批量插入数据 3.order by优化 4.group by优化 5.limit优化 6.count优化 count用法 7.update优化 1.插入数据 如果我们需要一次性往数据库表中插入多条记录,可以从以下三个方面进行优化 第一个:批量插入数据 Insert into tb_test va…...
python_level1.2
目录 一、变量 例如:小正方形——>大正方形 【1】第一次使用这个变量,所以说:定义一个变量length; 【2】:是赋值符号,不是等于符号。(只有赋值,该变量才会被创建)…...
Linux、Kylin OS挂载磁盘,开机自动加载
0.实验环境: 1.确定挂载目录,如果没有使用mkdir 进行创建: mkdir /data 2.查看磁盘 lsblk #列出所有可用的块设备df -T #查看磁盘文件系统类型 3.格式化成xfs文件系统 (这里以xfs为例,ext4类似) mkfs.xfs /dev/vdb 4.挂载到…...
