SpringBoot-集成Minio
官方文档:Kubernetes 的 MinIO 对象存储 — MinIO Object Storage for Kubernetes
一、简介
Minio 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。Minio是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。
二、引入依赖
<!-- MinIO -->
<dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.2.2</version>
</dependency>
<!-- Hutool -->
<dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.8.18</version>
</dependency>
Tips:MinIO依赖冲突问题 SpringBoot集成MinIO依赖冲突问题_Fly_Camel_Yu的博客-CSDN博客场景一: 使用minio8.3.0版本的依赖,报下列异常:An attempt was made to call a method that does not exist. The attempt was made from the following location: io.minio.S3Base.(S3Base.java:105)The following method did not exist: okhttp3.Requ...https://blog.csdn.net/qq_39974348/article/details/121742672
三、MinIO配置类
import io.minio.MinioClient;
import lombok.SneakyThrows;
import org.atm.dc.app.oss.props.MinioProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;import javax.annotation.Resource;/*** Minio配置类** @author meng*/
@Configuration
@EnableConfigurationProperties(MinioProperties.class)
@ConditionalOnProperty(value = "oss.name", havingValue = "minio")
public class MinioConfiguration {@Resourceprivate MinioProperties ossProperties;@Bean@SneakyThrowspublic MinioClient minioClient() {return MinioClient.builder().endpoint(ossProperties.getEndpoint()).credentials(ossProperties.getAccessKey(), ossProperties.getSecretKey()).build();}}
四、MinIO参数配置类
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;import java.util.List;/*** Minio参数配置类** @author meng*/
@Data
@ConfigurationProperties(prefix = MinioProperties.PREFIX)
public class MinioProperties {/*** 配置前缀*/public static final String PREFIX = "oss";/*** 对象存储名称*/private String name;/*** 对象存储服务的URL*/private String endpoint;/*** Access key 账户ID*/private String accessKey;/*** Secret key 密码*/private String secretKey;/*** 默认的存储桶名称*/private String bucketName = "meng";/*** 可上传的文件后缀名*/private List<String> fileExt;}
五、参数封装
import lombok.Data;
import java.util.Date;/*** OssFile** @author meng*/
@Data
public class OssFile {/*** 文件地址*/private String filePath;/*** 域名地址*/private String domain;/*** 文件名*/private String name;/*** 原始文件名*/private String originalName;/*** 文件hash值*/public String hash;/*** 文件大小*/private long size;/*** 文件上传时间*/private Date putTime;/*** 文件contentType*/private String contentType;
}
六、MinIO相关方法
import org.atm.dc.app.oss.model.OssFile;
import org.springframework.web.multipart.MultipartFile;import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.util.List;/*** OssTemplate抽象API** @author meng*/
public interface OssTemplate {/*** 存储桶是否存在** @param bucketName 存储桶名称* @return boolean*/boolean bucketExists(String bucketName);/*** 获取文件信息** @param fileName 存储桶文件名称* @return InputStream*/OssFile getOssInfo(String fileName);/*** 上传文件** @param folderName 上传的文件夹名称* @param fileName 上传文件名* @param file 上传文件类* @return BladeFile*/OssFile upLoadFile(String folderName, String fileName, MultipartFile file);/*** 上传文件** @param folderName 上传的文件夹名称* @param fileName 存储桶对象名称* @param suffix 文件后缀名* @param stream 文件流* @return BladeFile*/OssFile upLoadFile(String folderName, String fileName, String suffix, InputStream stream);/*** 删除文件** @param fileName 存储桶对象名称*/boolean removeFile(String fileName);/*** 批量删除文件** @param fileNames 存储桶对象名称集合*/boolean removeFiles(List<String> fileNames);/*** @Description: 下载文件* @Param response: 响应* @Param fileName: 文件名* @Param filePath: 文件路径* @return: void*/void downloadFile(HttpServletResponse response, String fileName, String filePath);
}
MinIOTemplate:
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.text.StrPool;
import cn.hutool.core.util.ObjectUtil;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.DeleteObject;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.atm.dc.app.common.Constants;
import org.atm.dc.app.oss.model.OssFile;
import org.atm.dc.app.oss.props.MinioProperties;
import org.atm.dc.app.oss.template.OssTemplate;
import org.atm.dc.app.util.FileInfoUtil;
import org.atm.dc.exception.BaseException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.List;
import java.util.stream.Stream;/*** MinIOTemplate** @author meng*/
@Slf4j
@Service
public class MinioTemplate implements OssTemplate {private Logger logger = LoggerFactory.getLogger(this.getClass());/*** MinIO客户端*/@Resourceprivate MinioClient client;/*** 配置类*/@Resourceprivate MinioProperties ossProperties;/*** 格式化时间*/private static final String DATE_FORMAT = "yyyyMMdd";/*** 字符集*/private static final String ENCODING = "UTF-8";/*** 存储桶是否存在** @param bucketName 存储桶名称* @return boolean*/@Overridepublic boolean bucketExists(String bucketName) {try {return client.bucketExists(BucketExistsArgs.builder().bucket(getBucketName(bucketName)).build());} catch (Exception e) {logger.error("minio bucketExists Exception:{}", e);}return false;}/*** @Description: 创建 存储桶* @Param bucketName: 存储桶名称* @return: void* @Author: wmh* @Date: 2023/8/2 11:28*/public void makeBucket(String bucketName) {try {if (!client.bucketExists(BucketExistsArgs.builder().bucket(getBucketName(bucketName)).build())) {client.makeBucket(MakeBucketArgs.builder().bucket(getBucketName(bucketName)).build());logger.info("minio makeBucket success bucketName:{}", bucketName);}} catch (Exception e) {logger.error("minio makeBucket Exception:{}", e);}}/*** 获取文件信息** @param fileName 存储桶文件名称* @return InputStream*/@Overridepublic OssFile getOssInfo(String fileName) {try {StatObjectResponse stat = client.statObject(StatObjectArgs.builder().bucket(getBucketName(ossProperties.getBucketName())).object(fileName).build());OssFile ossFile = new OssFile();ossFile.setName(ObjectUtil.isEmpty(stat.object()) ? fileName : stat.object());ossFile.setFilePath(ossFile.getName());ossFile.setDomain(getOssHost(ossProperties.getBucketName()));ossFile.setHash(String.valueOf(stat.hashCode()));ossFile.setSize(stat.size());ossFile.setPutTime(DateUtil.date(stat.lastModified().toLocalDateTime()));ossFile.setContentType(stat.contentType());return ossFile;} catch (Exception e) {logger.error("minio getOssInfo Exception:{}", e);}return null;}/*** 上传文件** @param folderName 上传的文件夹名称* @param fileName 上传文件名* @param file 上传文件类* @return BladeFile*/@Override@SneakyThrowspublic OssFile upLoadFile(String folderName, String fileName, MultipartFile file) {if (file == null || file.isEmpty()) {throw new BaseException("400", Constants.FILE_EMPTY);}// 文件大小if (file.getSize() > 5 * 1024 * 1024) {throw new BaseException("400", "文件大小不能超过5M");}String suffix = FileInfoUtil.getFileExtension(file.getOriginalFilename());// 文件后缀判断if (!CollUtil.contains(ossProperties.getFileExt(), suffix)) {String error = String.format("文件类型错误,目前支持[%s]等文件类型",String.join(",", ossProperties.getFileExt()));throw new BaseException("400", error);}try {return upLoadFile(folderName, fileName, suffix, file.getInputStream());} catch (Exception e) {logger.error("minio upLoadFile Exception:{}", e);throw new BaseException("400", "文件上传失败,请重新上传或联系管理员");}}/*** 上传文件** @param folderName 上传的文件夹名称* @param fileName 存储桶对象名称* @param suffix 文件后缀名* @param stream 文件流* @return BladeFile*/@Overridepublic OssFile upLoadFile(String folderName, String fileName, String suffix, InputStream stream) {try {return upLoadFile(ossProperties.getBucketName(), folderName, fileName, suffix, stream,"application/octet" + "-stream");} catch (Exception e) {logger.error("minio upLoadFile Exception:{}", e);}return null;}/*** @Description: 上传文件* @Param bucketName: 存储桶名称* @Param folderName: 上传的文件夹名称* @Param fileName: 上传文件名* @Param suffix: 文件后缀名* @Param stream: 文件流* @Param contentType: 文件类型* @Author: wmh* @Date: 2023/8/1 19:59*/@SneakyThrowspublic OssFile upLoadFile(String bucketName, String folderName, String fileName, String suffix, InputStream stream,String contentType) {if (!bucketExists(bucketName)) {logger.info("minio bucketName is not creat");makeBucket(bucketName);}OssFile file = new OssFile();String originalName = fileName;String filePath = getFilePath(folderName, fileName, suffix);client.putObject(PutObjectArgs.builder().bucket(getBucketName(bucketName)).object(filePath).stream(stream, stream.available(), -1).contentType(contentType).build());file.setOriginalName(originalName);file.setName(filePath);file.setDomain(getOssHost(bucketName));file.setFilePath(filePath);stream.close();logger.info("minio upLoadFile success, filePath:{}", filePath);return file;}/*** 删除文件** @param fileName 存储桶对象名称*/@Overridepublic boolean removeFile(String fileName) {try {client.removeObject(RemoveObjectArgs.builder().bucket(getBucketName(ossProperties.getBucketName())).object(fileName).build());logger.info("minio removeFile success, fileName:{}", fileName);return true;} catch (Exception e) {logger.error("minio removeFile fail, fileName:{}, Exception:{}", fileName, e);}return false;}/*** 批量删除文件** @param fileNames 存储桶对象名称集合*/@Overridepublic boolean removeFiles(List<String> fileNames) {try {Stream<DeleteObject> stream = fileNames.stream().map(DeleteObject::new);client.removeObjects(RemoveObjectsArgs.builder().bucket(getBucketName(ossProperties.getBucketName())).objects(stream::iterator).build());logger.info("minio removeFiles success, fileNames:{}", fileNames);return true;} catch (Exception e) {logger.error("minio removeFiles fail, fileNames:{}, Exception:{}", fileNames, e);}return false;}/*** @Description: 下载文件* @Param response: 响应* @Param fileName: 文件名* @Param filePath: 文件路径* @return: void* @Author: wmh* @Date: 2023/8/2 14:08*/@Overridepublic void downloadFile(HttpServletResponse response, String fileName, String filePath) {GetObjectResponse is = null;try {GetObjectArgs getObjectArgs =GetObjectArgs.builder().bucket(ossProperties.getBucketName()).object(filePath).build();is = client.getObject(getObjectArgs);// 设置文件ContentType类型,这样设置,会自动判断下载文件类型response.setContentType("application/x-msdownload");response.setCharacterEncoding(ENCODING);// 设置文件头:最后一个参数是设置下载的文件名并编码为UTF-8response.setHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(fileName, ENCODING));IoUtil.copy(is, response.getOutputStream());logger.info("minio downloadFile success, filePath:{}", filePath);} catch (Exception e) {logger.error("minio downloadFile Exception:{}", e);} finally {IoUtil.close(is);}}/*** 获取文件外链** @param bucketName bucket名称* @param fileName 文件名称* @param expires 过期时间* @return url*/public String getPresignedObjectUrl(String bucketName, String fileName, Integer expires) {String link = "";try {link = client.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().method(Method.GET).bucket(getBucketName(bucketName)).object(fileName).expiry(expires).build());} catch (Exception e) {logger.error("minio getPresignedObjectUrl is fail, fileName:{}", fileName);}return link;}/*** 根据规则生成存储桶名称规则** @param bucketName 存储桶名称* @return String*/private String getBucketName(String bucketName) {return bucketName;}/*** 根据规则生成文件路径** @param folderName 上传的文件夹名称* @param originalFilename 原始文件名* @param suffix 文件后缀名* @return string 上传的文件夹名称/yyyyMMdd/原始文件名_时间戳.文件后缀名*/private String getFilePath(String folderName, String originalFilename, String suffix) {return StrPool.SLASH + String.join(StrPool.SLASH, folderName, DateUtil.date().toString(DATE_FORMAT),originalFilename) + StrPool.C_UNDERLINE + DateUtil.current() + StrPool.DOT + suffix;}/*** 获取域名** @param bucketName 存储桶名称* @return String*/public String getOssHost(String bucketName) {return ossProperties.getEndpoint() + StrPool.SLASH + getBucketName(bucketName);}}
相关文章:
SpringBoot-集成Minio
官方文档:Kubernetes 的 MinIO 对象存储 — MinIO Object Storage for Kubernetes 一、简介 Minio 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频…...

【ML】cheatsheet
LR 原理与面试题目DT, Adaboost, GBDT, xgboost 原理 细节 与 例子 https://www.cnblogs.com/createMoMo/p/12635709.html xgboost挺详细的算法原理与例子 https://zhuanlan.zhihu.com/p/660468945 着重lightgbm就xgboost的改善方向 https://zhuanlan.zhihu.com/p/366952043机器…...

【字符串】【将字符数组转为字符串】Leetcode 122 路径加密
【将字符数组转为字符串】Leetcode 122 路径加密 解法1 在Java中,char数组没有直接的toString()方法来将其转换为字符串。如果你想将char数组转换为字符串,可以使用String类的构造函数来实现: ⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐…...
网络基础知识100问
1.什么是链接? 链接是指两个设备之间的连接。它包括用于一个设备能够与另一个设备通信的电缆类型和协议。 2.OSI 参考模型的层次是什么? 有 7 个 OSI 层:物理层,数据链路层,网络层,传输层,会话层,表示…...

女孩子就是要打扮漂亮,让童年不留遗憾
好的衣服当然要分享给好看的人啦! 百搭圆领卫衣,经典版型不挑人穿 复合奥利绒面料,罗纹收口设计 时尚百搭怎么穿都好看 单穿内搭都可以 卡通鹅真的好可爱 宝贝穿上去真的元气满满哦...
实现目录数据的上移(up)、下移(down)、置顶(top)、置底(bottom)的操作
ApiOperation("8-15 交接班-标签设置排序")ApiImplicitParams({ApiImplicitParam(name "id", value "id", dataType "string", required true),ApiImplicitParam(name "orgnCode", value "机构代码", dataT…...

Ubuntu 常用命令
文章目录 Linux 目录结构常用命令ls:查看目录内容pwd:查看当前目录绝对路径cd:切换目录mkdir:创建目录rm:删除文件/目录touch:创建空文件mv:移动和重命名文件/目录cp:复制粘贴cat&am…...

如何空手套白狼?一口气省7K再抓住一个7K起步的工作?
今日话题,教你如何省七千再得到一个七千起步的技能!现在网络行业已经是全世界重点发展的目标,开发行业更是各个企业重点培养,但是在学校教的网络知识太基础太老掉牙?报班随便就是小一万该如何是好呢?解决方…...
电脑主机如何选择内存条
选择计算机主机的内存模块(内存条)通常需要考虑以下因素: 类型和代数(DDR3、DDR4、DDR5等):您的主板和处理器支持的内存类型非常重要。确保内存条的类型与您的主板和处理器兼容。 容量:内存容量…...

计算机考研自命题(5)
1、C语言–求和 1、展开式求和。输入一个实数x,计算并输出下式的和,直到最后一项的绝对值小于0.00001.计算结果保留2位小数,试编程。 S x x/2! x/3! … /* 算法思想:定义一个求阶乘的函数fact(), 头文件调…...
【原创】c语言4种字符串函数的代码测试
c语言4种字符串函数的代码测试 1.字符串拼接strcat [contact] 2.字符串复制strcpy 3.带参数的字符串复制strncpy 4.字符串比较strcmp:比第一个不同字母的ascii码,如acb>abc #include<stdio.h> #include<string.h>int main() {char s1[]&…...

扩散模型学习——代码学习
文章目录 引言正文UNet网络结构训练方法DDPM采样方法讲解Context上下文信息添加DDIM的方法详解 总结参考 引言 这是第一次接触扩散模型,为了学习,这里好好分析一下他的代码 正文 UNet网络结构 这部分主要是定义一下网络结构,以及相关的网…...

redis 数据结构
一、为什么要扒一下底层技术 首先我是一个解决方案工程师,为什么要看redis底层的设计呢?总结下来分几点: 1. 让系统跑起来更放心 2. 面试中可以对跟对面的牛马侃大山、吹🐮 3. 虚一点,举一反三,学习一下…...
node.js中express框架cookie-parser包设置cookie的问题
后端使用node.js express cookie-parser技术栈设置cookie的时候出现了无法成功设置的问题 前端发送axios请求部分代码: axios({method: "post",data: {content: remark,relatedArticles: relatedArticleId,userId: userId,userEmail: userEmail,topRema…...

Docker命令手册
大家好,我叫徐锦桐,个人博客地址为www.xujintong.com。平时记录一下学习计算机过程中获取的知识,还有日常折腾的经验,欢迎大家访问。 记录平时用的比较多的Docker命令。 docker学习地址 1、docker停止并删除运行的容器 首先查看…...

Selenium+Pytest自动化测试框架详解
前言 selenium自动化 pytest测试框架 本章你需要 一定的python基础——至少明白类与对象,封装继承;一定的selenium基础——本篇不讲selenium,不会的可以自己去看selenium中文翻译网 一、测试框架简介 测试框架有什么优点 代码复用率高&…...

CentOS7安装部署CDH6.2.1
文章目录 CentOS7安装部署CDH6.2.1一、前言1.简介2.架构3.环境 二、环境准备1.部署服务器2.安装包准备3.修改机器名4.关闭防火墙5.关闭 SELinux6.Hosts文件7.limits文件8.设置swap空间9.关闭透明巨页内存10.免密登录 三、安装CM管理端1.安装第三方依赖包2.安装Oracle的JDK3.安装…...

海思Hi3519DV500边缘计算盒子-英码IVP09A,双核A55 64位处理器
产品简介 IVP09A是英码科技推出的边缘计算智能工作站,搭载双核 Cortex-A55 架构AI 处理器;内置高效的神经网络推理引擎,提供2.5TopsNPU算力;支持多路视频图像识别硬件加速。IVP09A,高效能低成本、稳定易开发、多点布线、联网管控…...

理解数据库
文章目录 一、了解什么是信息1.1 信息和数据1.1.1 信息 (information)1.1.2 数据 (Data) 1.2 数据处理 二、如何描述数据具备的信息2.1 数据库的一些术语: 三、数据模型3.1 概念模型 E-R,是对信息世界的建模…...

RHCE---Shell基础 2
文章目录 目录 文章目录 前言 一.变量 概述 定义 自定义变量 环境变量 概述: 定义环境变量: 位置变量 "$*"会把所有位置参数当成一个整体(或者说当成一个单词 变量的赋值和作用域 read 命令 变量和引号 变量的作用域 变…...
Java 语言特性(面试系列2)
一、SQL 基础 1. 复杂查询 (1)连接查询(JOIN) 内连接(INNER JOIN):返回两表匹配的记录。 SELECT e.name, d.dept_name FROM employees e INNER JOIN departments d ON e.dept_id d.dept_id; 左…...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...
macOS多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用
文章目录 问题现象问题原因解决办法 问题现象 macOS启动台(Launchpad)多出来了:Google云端硬盘、YouTube、表格、幻灯片、Gmail、Google文档等应用。 问题原因 很明显,都是Google家的办公全家桶。这些应用并不是通过独立安装的…...
使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度
文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join
纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...