当前位置: 首页 > news >正文

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

官方文档&#xff1a;Kubernetes 的 MinIO 对象存储 — MinIO Object Storage for Kubernetes 一、简介 Minio 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口&#xff0c;非常适合于存储大容量非结构化的数据&#xff0c;例如图片、视频…...

【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中&#xff0c;char数组没有直接的toString()方法来将其转换为字符串。如果你想将char数组转换为字符串&#xff0c;可以使用String类的构造函数来实现&#xff1a; ⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐️⭐…...

网络基础知识100问

1.什么是链接? 链接是指两个设备之间的连接。它包括用于一个设备能够与另一个设备通信的电缆类型和协议。 2.OSI 参考模型的层次是什么? 有 7 个 OSI 层&#xff1a;物理层&#xff0c;数据链路层&#xff0c;网络层&#xff0c;传输层&#xff0c;会话层&#xff0c;表示…...

女孩子就是要打扮漂亮,让童年不留遗憾

好的衣服当然要分享给好看的人啦&#xff01; 百搭圆领卫衣&#xff0c;经典版型不挑人穿 复合奥利绒面料&#xff0c;罗纹收口设计 时尚百搭怎么穿都好看 单穿内搭都可以 卡通鹅真的好可爱 宝贝穿上去真的元气满满哦...

实现目录数据的上移(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&#xff1a;查看目录内容pwd&#xff1a;查看当前目录绝对路径cd&#xff1a;切换目录mkdir&#xff1a;创建目录rm&#xff1a;删除文件/目录touch&#xff1a;创建空文件mv&#xff1a;移动和重命名文件/目录cp&#xff1a;复制粘贴cat&am…...

如何空手套白狼?一口气省7K再抓住一个7K起步的工作?

今日话题&#xff0c;教你如何省七千再得到一个七千起步的技能&#xff01;现在网络行业已经是全世界重点发展的目标&#xff0c;开发行业更是各个企业重点培养&#xff0c;但是在学校教的网络知识太基础太老掉牙&#xff1f;报班随便就是小一万该如何是好呢&#xff1f;解决方…...

电脑主机如何选择内存条

选择计算机主机的内存模块&#xff08;内存条&#xff09;通常需要考虑以下因素&#xff1a; 类型和代数&#xff08;DDR3、DDR4、DDR5等&#xff09;&#xff1a;您的主板和处理器支持的内存类型非常重要。确保内存条的类型与您的主板和处理器兼容。 容量&#xff1a;内存容量…...

计算机考研自命题(5)

1、C语言–求和 1、展开式求和。输入一个实数x&#xff0c;计算并输出下式的和&#xff0c;直到最后一项的绝对值小于0.00001.计算结果保留2位小数&#xff0c;试编程。 S x x/2&#xff01; x/3&#xff01; … /* 算法思想&#xff1a;定义一个求阶乘的函数fact(), 头文件调…...

【原创】c语言4种字符串函数的代码测试

c语言4种字符串函数的代码测试 1.字符串拼接strcat [contact] 2.字符串复制strcpy 3.带参数的字符串复制strncpy 4.字符串比较strcmp&#xff1a;比第一个不同字母的ascii码&#xff0c;如acb>abc #include<stdio.h> #include<string.h>int main() {char s1[]&…...

扩散模型学习——代码学习

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

redis 数据结构

一、为什么要扒一下底层技术 首先我是一个解决方案工程师&#xff0c;为什么要看redis底层的设计呢&#xff1f;总结下来分几点&#xff1a; 1. 让系统跑起来更放心 2. 面试中可以对跟对面的牛马侃大山、吹&#x1f42e; 3. 虚一点&#xff0c;举一反三&#xff0c;学习一下…...

node.js中express框架cookie-parser包设置cookie的问题

后端使用node.js express cookie-parser技术栈设置cookie的时候出现了无法成功设置的问题 前端发送axios请求部分代码&#xff1a; axios({method: "post",data: {content: remark,relatedArticles: relatedArticleId,userId: userId,userEmail: userEmail,topRema…...

Docker命令手册

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

Selenium+Pytest自动化测试框架详解

前言 selenium自动化 pytest测试框架 本章你需要 一定的python基础——至少明白类与对象&#xff0c;封装继承&#xff1b;一定的selenium基础——本篇不讲selenium&#xff0c;不会的可以自己去看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是英码科技推出的边缘计算智能工作站&#xff0c;搭载双核 Cortex-A55 架构AI 处理器&#xff1b;内置高效的神经网络推理引擎&#xff0c;提供2.5TopsNPU算力;支持多路视频图像识别硬件加速。IVP09A&#xff0c;高效能低成本、稳定易开发、多点布线、联网管控…...

理解数据库

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

RHCE---Shell基础 2

文章目录 目录 文章目录 前言 一.变量 概述 定义 自定义变量 环境变量 概述&#xff1a; 定义环境变量&#xff1a; 位置变量 "$*"会把所有位置参数当成一个整体&#xff08;或者说当成一个单词 变量的赋值和作用域 read 命令 变量和引号 变量的作用域 变…...

synchronized 学习

学习源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖&#xff0c;也要考虑性能问题&#xff08;场景&#xff09; 2.常见面试问题&#xff1a; sync出…...

Golang dig框架与GraphQL的完美结合

将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用&#xff0c;可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器&#xff0c;能够帮助开发者更好地管理复杂的依赖关系&#xff0c;而 GraphQL 则是一种用于 API 的查询语言&#xff0c;能够提…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

css的定位(position)详解:相对定位 绝对定位 固定定位

在 CSS 中&#xff0c;元素的定位通过 position 属性控制&#xff0c;共有 5 种定位模式&#xff1a;static&#xff08;静态定位&#xff09;、relative&#xff08;相对定位&#xff09;、absolute&#xff08;绝对定位&#xff09;、fixed&#xff08;固定定位&#xff09;和…...

LINUX 69 FTP 客服管理系统 man 5 /etc/vsftpd/vsftpd.conf

FTP 客服管理系统 实现kefu123登录&#xff0c;不允许匿名访问&#xff0c;kefu只能访问/data/kefu目录&#xff0c;不能查看其他目录 创建账号密码 useradd kefu echo 123|passwd -stdin kefu [rootcode caozx26420]# echo 123|passwd --stdin kefu 更改用户 kefu 的密码…...

深入浅出深度学习基础:从感知机到全连接神经网络的核心原理与应用

文章目录 前言一、感知机 (Perceptron)1.1 基础介绍1.1.1 感知机是什么&#xff1f;1.1.2 感知机的工作原理 1.2 感知机的简单应用&#xff1a;基本逻辑门1.2.1 逻辑与 (Logic AND)1.2.2 逻辑或 (Logic OR)1.2.3 逻辑与非 (Logic NAND) 1.3 感知机的实现1.3.1 简单实现 (基于阈…...

Linux 下 DMA 内存映射浅析

序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存&#xff0c;但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程&#xff0c;可以参考这篇文章&#xff0c;我觉得写的非常…...

aardio 自动识别验证码输入

技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”&#xff0c;于是尝试整合图像识别与网页自动化技术&#xff0c;完成了这套模拟登录流程。核心思路是&#xff1a;截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...

【java面试】微服务篇

【java面试】微服务篇 一、总体框架二、Springcloud&#xff08;一&#xff09;Springcloud五大组件&#xff08;二&#xff09;服务注册和发现1、Eureka2、Nacos &#xff08;三&#xff09;负载均衡1、Ribbon负载均衡流程2、Ribbon负载均衡策略3、自定义负载均衡策略4、总结 …...

云原生时代的系统设计:架构转型的战略支点

&#x1f4dd;个人主页&#x1f339;&#xff1a;一ge科研小菜鸡-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 一、云原生的崛起&#xff1a;技术趋势与现实需求的交汇 随着企业业务的互联网化、全球化、智能化持续加深&#xff0c;传统的 I…...