spring boot 整合 minio存储 【使用篇】
zi导入依赖
<!--minio--><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.0.3</version></dependency>
yml配置(默认配置)
spring:# 配置文件上传大小限制servlet:multipart:max-file-size: 200MBmax-request-size: 200MB
minio:endpoint: http://127.0.0.1:9000accessKey: minioadminsecretKey: minioadminbucketName: bucket-qhj
一一对应
补充:由于每次启动minio.exe要进入对应目录,很麻烦,所以可以写个批处理文件.bat
D: 代表进入D盘
cd D:\Program Files\minio 代表进入你minio.exe存放的根目录
minio.exe server D:\MineFile\zuoye\xm\minioFile --console-address ":9999" 在D:\MineFile\zuoye\xm\minioFile文件夹执行,并且增加额外端口’9999‘
@echo off
echo.
echo [信息] 运行MinIO文服务器。
echo.title minioD:
cd D:\Program Files\miniominio.exe server D:\MineFile\zuoye\xm\minioFile --console-address ":9999"
pause
配置类
注入客户端配置放入bean
使用value注解装配yml配置文件中的值
@Data
@Component
public class MinIoClientConfig {@Value("${minio.endpoint}")private String endpoint;@Value("${minio.accessKey}")private String accessKey;@Value("${minio.secretKey}")private String secretKey;/*** 注入minio 客户端* @return*/@Beanpublic MinioClient minioClient(){return MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build();}}
实体类
@Data
public class ObjectItem {private String objectName;private Long size;
}
工具类
根据实体类
/*** @description: minio工具类* @version:3.0*/
@Component
public class MinioUtilS {@Autowiredprivate MinioClient minioClient;@Value("${minio.bucketName}")private String bucketName;/*** description: 判断bucket是否存在,不存在则创建** @return: void*/public void existBucket(String name) {try {boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());if (!exists) {minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());}} catch (Exception e) {e.printStackTrace();}}/*** 创建存储bucket* @param bucketName 存储bucket名称* @return Boolean*/public Boolean makeBucket(String bucketName) {try {minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** 删除存储bucket* @param bucketName 存储bucket名称* @return Boolean*/public Boolean removeBucket(String bucketName) {try {minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());} catch (Exception e) {e.printStackTrace();return false;}return true;}/*** description: 上传文件** @param multipartFile* @return: java.lang.String*/public List<String> upload(MultipartFile[] multipartFile) {List<String> names = new ArrayList<>(multipartFile.length);for (MultipartFile file : multipartFile) {String fileName = file.getOriginalFilename();String[] split = fileName.split("\\.");if (split.length > 1) {fileName = split[0] + "_" + System.currentTimeMillis() + "." + split[1];} else {fileName = fileName + System.currentTimeMillis();}InputStream in = null;try {in = file.getInputStream();minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(in, in.available(), -1).contentType(file.getContentType()).build());} catch (Exception e) {e.printStackTrace();} finally {if (in != null) {try {in.close();} catch (IOException e) {e.printStackTrace();}}}names.add(fileName);}return names;}/*** description: 下载文件** @param fileName* @return: org.springframework.http.ResponseEntity<byte [ ]>*/public ResponseEntity<byte[]> download(String fileName) {ResponseEntity<byte[]> responseEntity = null;InputStream in = null;ByteArrayOutputStream out = null;try {in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());out = new ByteArrayOutputStream();IOUtils.copy(in, out);//封装返回值byte[] bytes = out.toByteArray();HttpHeaders headers = new HttpHeaders();try {headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));} catch (UnsupportedEncodingException e) {e.printStackTrace();}headers.setContentLength(bytes.length);headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);headers.setAccessControlExposeHeaders(Arrays.asList("*"));responseEntity = new ResponseEntity<byte[]>(bytes, headers, HttpStatus.OK);} catch (Exception e) {e.printStackTrace();} finally {try {if (in != null) {try {in.close();} catch (IOException e) {e.printStackTrace();}}if (out != null) {out.close();}} catch (IOException e) {e.printStackTrace();}}return responseEntity;}/*** 查看文件对象* @param bucketName 存储bucket名称* @return 存储bucket内文件对象信息*/public List<ObjectItem> listObjects(String bucketName) {Iterable<Result<Item>> results = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).build());List<ObjectItem> objectItems = new ArrayList<>();try {for (Result<Item> result : results) {Item item = result.get();ObjectItem objectItem = new ObjectItem();objectItem.setObjectName(item.objectName());objectItem.setSize(item.size());objectItems.add(objectItem);}} catch (Exception e) {e.printStackTrace();return null;}return objectItems;}/*** 批量删除文件对象* @param bucketName 存储bucket名称* @param objects 对象名称集合*/public Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> objects) {List<DeleteObject> dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList());Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(dos).build());return results;}
}
控制器类
返回可访问的url
@RestController
@Slf4j
@RequestMapping("/fileSave")
public class MinioController {@Autowiredprivate MinIoUtil minIoUtil;@Autowiredprivate MinioUtilS minioUtilS;@Value("${minio.endpoint}")private String address;@Value("${minio.bucketName}")private String bucketName;@RequestMapping("/fileInMinio")public R uploadInMinio(MultipartFile file) {List<String> upload = minioUtils.upload(new MultipartFile[]{file});String url = address + "/" + bucketName + "/" + upload.get(0);return R.ok().put("filePath", url);}}
上传后获取链接为
访问报错
解决方式
来到minio控制台
打开你正在使用的bucket
修改访问权限为public就行
重新访问链接,应该就能查看
前端代码
通过父组件导入子组件,注册子组件,引用子组件弹出
父组件调用方法
// 文件导入importExcel () {this.uploadFileVisible = truethis.$nextTick(() => {this.$refs.uploadFile.init()})}
子组件vue页面
<template><el-dialogtitle="维修处理":close-on-click-modal="true":visible.sync="visible"><el-selectsize="small"v-model="uploadMethodValue"placeholder="请选择上传方式"clearable=""filterable><el-optionv-for="d in uploadMethod":key="d.value":label="d.name":value="d.value"></el-option></el-select><el-uploadclass="upload-demo"ref="upload"dragaction="#":on-change="handleChangeSelect":on-exceed="handleExceed":file-list="fileList":limit="1"multiple:auto-upload="false"><i class="el-icon-upload"></i><div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div><divclass="el-upload__tip"slot="tip">只能上传jpg/png文件,且不超过500kb</div><divclass="el-upload__tip"slot="tip">访问路径:{{ filePath }}</div></el-upload></el-dialog>
</template><script>
export default {data () {return {// 对话框显示状态visible: false,uploadMethod: [{ name: '本地存储', value: '1' },{ name: 'oss存储', value: '2' },{ name: 'miniio', value: '3' }],// 选中的上传方式uploadMethodValue: '',// 表单数据dataForm: {},filePath: '',fileList: []}},methods: {// 初始化方法init () {this.visible = truethis.filePath = ''},// 文件超出个数提示handleExceed (files, fileList) {this.$message.warning(`当前限制选择 1 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`)},handleChangeSelect (file) {if (this.uploadMethodValue === '1') {// 本地存储this.handleChange(file, '/fileSave/file')} else if (this.uploadMethodValue === '2') {// oss存储this.handleChange(file, '/fileSave/fileInOSS')} else if (this.uploadMethodValue === '3') {// miniiothis.handleChange(file, '/fileSave/fileInMinio')}},handleChange (file, urlSelect) {let formData = new FormData()formData.append('file', file.raw) // 传文件this.$http({url: this.$http.adornUrl(urlSelect),method: 'post',data: formData,headers: {'Content-Type': 'multipart/form-data'}}).then(({ data }) => {if (data && data.code === 0) {this.filePath = data.filePaththis.$message({message: '操作成功',type: 'success',duration: 1500})} else {this.$message.error(data.msg)}this.$refs.upload.clearFiles() // 清除文件})}}
}</script>
拓展学习
MultipartFile
MultipartFile为org.springframework.web.mutipart包下的一个类,也就是说如果想使用MultipartFile这个类就必须引入spring框架,换句话说,如果想在项目中使用MultipartFile这个类,那么项目必须要使用spring框架才可以,否则无法引入这个类。
一般来讲使用MultipartFile这个类主要是来实现以表单的形式进行文件上传功能。
MiltipartFile类注释说明
第一句:一种可以接收使用多种请求方式来进行上传文件的代表形式。也就是说,如果你想用spring框架来实现项目中的文件上传功能,则MultipartFile可能是最合适的选择,而这里提到的多种请求方式则可以通俗理解为以表单的形式提交。
第二句:这个文件内容可以存储到内存中或者存储在磁盘的临时位置上。
第三句:无论发生哪种情况,用户都可以自由地拷贝文件内容到session存储中,或者以一种永久存储的形式进行存储,如果有需要的话。
第四句:这种临时性的存储在请求结束之后将会被清除掉。
类中方法
getName方法
获取的是前后端约定的传入文件的参数的名称,在SpringBoot后台中则是通过@Param("uploadFile") 注解定义的内容。
getOriginalFileName方法
getOriginalFileName方法获取的是文件的完整名称,包括文件名称+文件拓展名。
getContentType方法
getContentType方法获取的是文件的类型,注意是文件的类型,不是文件的拓展名。
getBytes方法
getBytes方法用来将文件转换成一种字节数组的方式进行传输,会抛出IOException异常。
getInputStream方法
getInputStream方法用来将文件转换成输入流的形式来传输文件,会抛出IOException异常。
transferTo方法
transferTo方法用来将接收文件传输到给定目标路径,会抛出IOException、IllegalStateException异常。该方法在实际项目开发中使用较少。
相关文章:

spring boot 整合 minio存储 【使用篇】
zi导入依赖 <!--minio--><dependency><groupId>io.minio</groupId><artifactId>minio</artifactId><version>8.0.3</version></dependency> yml配置(默认配置) spring:# 配置文件上传大小限制s…...

【Redis】深入理解 Redis 常用数据类型源码及底层实现(5.详解List数据结构)
本文是深入理解 Redis 常用数据类型源码及底层实现系列的第5篇~前4篇可移步( ̄∇ ̄)/ 【Redis】深入理解 Redis 常用数据类型源码及底层实现(1.结构与源码概述)-CSDN博客 【Redis】深入理解 Redis 常用数据类型源码及底…...

Vue+Flask电商后台管理系统
在这个项目中,我们将结合Vue.js前端框架和python后端框架Flask,打造一个功能强大、易于使用的电商后台管理系统 项目演示视频: VueFlask项目 目录 前端环境(Vue.js): 后端环境(python-Flask&…...
SpringBoot保姆级入门文档
目录 1、SpringBoot的优点 2、和Spring、SpringMVC的对比 3、Xml 和 JavaConfig 1、SpringBoot的优点 2、和Spring、SpringMVC的对比 3、Xml 和 JavaConfig Spring 使用 Xml 作为容器配置文件,在 3.0 以后加入了 JavaConfig,使用 java 类做配置文件使…...
Springboot同一台服务器部署多个项目,导致redis混淆,如何根据不同项目区分
在Spring Boot应用中,如果在同一台服务器上部署了多个项目,并且每个项目都使用Redis作为缓存或存储,为了避免Redis数据混淆,你需要确保各个项目在访问Redis时使用不同的数据库索引号、键前缀或者连接配置。 以下是一些区分不同项目Redis数据的方法: 使用不同数据库索引:…...

redis启动错误
错误: Creating Server TCP listening socket 127.0.0.1:6379: bind: No error redis-server.exe redis.windows.conf redis-cli.exe shutdown auth "yourpassword"...

单片机烧录方式 -- IAP、ISP和ICP
目录 背景 1 什么是ICP 2 什么是ISP 3 什么是IAP 4 总结 背景 对于51单片机,我们使用STC-ISP上位机软件通过串口进行程序的烧写;对于STM32系列单片机,我们既可以通过串口烧写程序,也能通过JLink或是STLink进行程序的烧写&am…...
数据结构(C语言版)01
//顺序存储 int main(){ int ans[5]{1,1,1,1,3};//定义并初始化 printf("%d",ans[4]); return 0; } //链式存储 Typdef struct Lnode{ElemType data;struct Lnode *next; }Londe,*LinKlist;Londe *L; L(LinkList)malloc(sizeof(Lnode)); A->nextB;B->nextC;…...
Node.js-文件读取输入
Node.js-文件读取输入 fs模块(操作文件的模块) 读取 fs.readFile(path[, options], callback);[]里面 是可选参数,表示以什么样的编码 格式读取path是路径callback表示读取完成后的回调函数 例子 fs.readFile (‘./files/11.txt…...

时隔一年的测评:gpt3.5发展到什么程度了?
名人说:一花独放不是春,百花齐放花满园。——《增广贤文》 作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 目录 一、简要介绍1、chatgpt是什么?2、主要特点3、工作原理4、应用限制5、使…...

[RCTF2015]EasySQL1 题目分析与详解
一、题目介绍: 1、题目来源: BUUCTF网址 2、题目介绍: 拿到flag。 二、解题思路: 我们发现题目首页有登录和注册账号两个选项,我们首先尝试注册账号,尝试注册username为admin的账号,输入密码…...

开源的 Python 数据分析库Pandas 简介
阅读本文之前请参阅-----如何系统的自学python Pandas 是一个开源的 Python 数据分析库,它提供了高性能、易用的数据结构和数据分析工具。Pandas 特别适合处理表格数据,例如时间序列数据、异构数据等。以下是对 Pandas 的简明扼要的介绍,包括…...

LeetCode 2125.银行中的激光束数量
银行内部的防盗安全装置已经激活。给你一个下标从 0 开始的二进制字符串数组 bank ,表示银行的平面图,这是一个大小为 m x n 的二维矩阵。 bank[i] 表示第 i 行的设备分布,由若干 ‘0’ 和若干 ‘1’ 组成。‘0’ 表示单元格是空的࿰…...
【探索AI】Sora - 探索AI视频模型的无限可能
Sora - 探索AI视频模型的无限可能 随着人工智能技术的飞速发展,AI视频模型已成为科技领域的新热点。而在这个浪潮中,OpenAI推出的首个AI视频模型Sora,以其卓越的性能和前瞻性的技术,引领着AI视频领域的创新发展。让我们将一起探讨…...

NGINX的重写与反向代理机制解析
目录 引言 一、重写功能 (一)if指令 1.判断访问使用的协议 2.判断文件 (二)return指令 1.设置返回状态码 2.返回指定内容 3.指定URL (三)set指令 1.手动输入变量值 2.调用其它变量值为自定义变…...

JVM的深入理解
1、JVM(Java虚拟机):我们java编译时候,下通过把avac把.java文件转换成.class文件(字节码文件),之后我们通过jvm把字节码文件转换成对应的cpu能识别的机器指令(翻译官角色)…...

JavaWeb——007MYSQL(DQL多表设计)
# 数据库开发-MySQL 一级目录二级目录三级目录 1. 数据库操作-DQL1.1 介绍1.2 语法1.3 基本查询1.4 条件查询1.5 聚合函数1.6 分组查询1.7 排序查询1.8 分页查询1.9 案例1.9.1 案例一1.9.2 案例二 2. 多表设计2.1 一对多2.1.1 表设计2.1.2 外键约束 2.2 一对一2.3 多对多2.4 案…...
深度学习500问——Chapter01:数学基础
文章目录 前言 1.1 向量和矩阵 1.1.1 标量、向量、矩阵、张量之间的联系 1.1.2 张量与矩阵的区别 1.1.3 矩阵和向量相乘结果 1.1.4 向量和矩阵的范数归纳 1.1.5 如何判断一个矩阵为正定 1.2 导数和偏导数 1.2.1 导数偏导计算 1.2.2 导数和偏导数有什么区别 1.3 特征值和特征向量…...

day03_登录注销(前端接入登录,异常处理, 图片验证码,获取用户信息接口,退出功能)
文章目录 1. 前端接入登录1.1 修改前端代码1.2 跨域请求1.2.1 跨域请求简介1.2.2 COSR概述CORS简介CORS原理 1.2.3 CORS解决跨域 2. 异常处理2.1 提示空消息分析2.2 系统异常分类2.3 异常处理2.2.1 方案一2.2.2 方案二 3. 图片验证码3.1 图片验证码意义3.2 实现思路3.3 后端接口…...

k8s初始化报错 [ERROR CRI]: container runtime is not running: ......
一、环境参数 linux系统为centos7kubernetes版本为v1.28.2containerd版本为1.6.28 二、报错内容 执行初始化命令kubeadm init命令时报错,内容如下 error execution phase preflight: [preflight] Some fatal errors occurred:[ERROR CRI]: container runtime is…...

STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

Golang——9、反射和文件操作
反射和文件操作 1、反射1.1、reflect.TypeOf()获取任意值的类型对象1.2、reflect.ValueOf()1.3、结构体反射 2、文件操作2.1、os.Open()打开文件2.2、方式一:使用Read()读取文件2.3、方式二:bufio读取文件2.4、方式三:os.ReadFile读取2.5、写…...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...

Vue3 PC端 UI组件库我更推荐Naive UI
一、Vue3生态现状与UI库选择的重要性 随着Vue3的稳定发布和Composition API的广泛采用,前端开发者面临着UI组件库的重新选择。一个好的UI库不仅能提升开发效率,还能确保项目的长期可维护性。本文将对比三大主流Vue3 UI库(Naive UI、Element …...

向量几何的二元性:叉乘模长与内积投影的深层联系
在数学与物理的空间世界中,向量运算构成了理解几何结构的基石。叉乘(外积)与点积(内积)作为向量代数的两大支柱,表面上呈现出截然不同的几何意义与代数形式,却在深层次上揭示了向量间相互作用的…...

云原生安全实战:API网关Envoy的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关 作为微服务架构的统一入口,负责路由转发、安全控制、流量管理等核心功能。 2. Envoy 由Lyft开源的高性能云原生…...
Python的__call__ 方法
在 Python 中,__call__ 是一个特殊的魔术方法(magic method),它允许一个类的实例像函数一样被调用。当你在一个对象后面加上 () 并执行时(例如 obj()),Python 会自动调用该对象的 __call__ 方法…...
「Java基本语法」变量的使用
变量定义 变量是程序中存储数据的容器,用于保存可变的数据值。在Java中,变量必须先声明后使用,声明时需指定变量的数据类型和变量名。 语法 数据类型 变量名 [ 初始值]; 示例:声明与初始化 public class VariableDemo {publi…...

智警杯备赛--excel模块
数据透视与图表制作 创建步骤 创建 1.在Excel的插入或者数据标签页下找到数据透视表的按钮 2.将数据放进“请选择单元格区域“中,点击确定 这是最终结果,但是由于环境启不了,这里用的是自己的excel,真实的环境中的excel根据实训…...
标注工具核心架构分析——主窗口的图像显示
🏗️ 标注工具核心架构分析 📋 系统概述 主要有两个核心类,采用经典的 Scene-View 架构模式: 🎯 核心类结构 1. AnnotationScene (QGraphicsScene子类) 主要负责标注场景的管理和交互 🔧 关键函数&…...