注册

这年头不会还有谁没碰过minio的吧?这可太...🤡


🏆本文收录于「滚雪球学Spring Boot」专栏,专业攻坚指数级提升持续更新中,up!up!up!!



🥝 前言:文件存储那些“坑”,你踩过几个?


  想象一下,你正在开发一个新项目,老板突然拍着桌子跟你说:“咱这个项目得支持海量文件存储,用户随时上传随时下载,成本要低,性能要高,安全也不能落下!”你抓了抓头发,盯着屏幕陷入沉思,传统文件系统?太笨重。云存储?预算超标。就在你一筹莫展时,MinIO横空出世,仿佛一道曙光,照亮了你前行的路。


  MinIO,这款开源的对象存储系统,以其高性能、易扩展、S3兼容性等优点,迅速成为开发者圈中的“香饽饽”。如果你用Spring Boot开发项目,想要高效管理文件存储,那么接下来的内容会让你大呼过瘾。



90468c093cb420f26a0e20b8944b8c1a.jpg

🍇 MinIO是什么?


  MinIO,是一款以高性能、轻量级著称的对象存储服务。它完全兼容Amazon S3 API,支持大规模非结构化数据的存储,适合图片、视频、日志、备份等海量数据的管理需求。


  简单点说,它就是你的“私人云存储”,但没有昂贵的费用和复杂的运维。不论是几百GB还是上百TB的数据,MinIO都能轻松搞定。


🍒 MinIO的“秘密武器”



  • 开源免费:没有隐藏费用,企业也能无压力使用。
  • S3 API兼容:现有的S3工具可以无缝衔接。
  • 性能炸裂:每秒高达数十GB的吞吐量,轻松应对高并发。
  • 易部署,易维护:几行命令搞定,开发小白也能轻松上手。

🍅 为什么选择MinIO?


  有人可能会问:“为啥不用传统的文件系统?” 传统文件系统确实在小规模存储中还算凑合,但当你面对动辄几百GB甚至TB级的数据时,传统方案的缺点就暴露无遗了。管理难、性能低、扩展性差……而MinIO正是为了解决这些痛点而生。


d3db88d34d7a5293f65ca79406a1e7d2.jpg

🥝 MinIO能给你什么?



  1. 超高性价比:无需支付昂贵的存储服务费用,MinIO让你拥有“云存储”的体验,却不需要“云存储”的钱包。
  2. 弹性扩展:无论是初创团队还是大型企业,MinIO都能根据业务规模灵活扩展,绝不让存储成为发展瓶颈。
  3. 高可用性:MinIO支持分布式部署,即使某个节点故障,数据依然安全无忧。


选择MinIO,就是选择一种面向未来的存储方式。



🥑 MinIO核心概念


● 对象(Object):对象是实际的数据单元,例如:上传的图片。


● 存储桶(Bucket):存储桶是用于组织对象的名称空间,类似于文件夹。每个存储桶可以包含多个对象(文件)。


● 端点(Endpoint):MinIO服务器的网络地址,用于访问存储桶和对象。例如:http://192.168.10.100:9000 , 注意:9000为 MinIO的API默认端口。


● AccessKey 和Secret Key:



  • AccessKey:用于标识和验证访问者身份的唯一标识符,相当于用户名。
  • Secret Key:与AccessKey关联的密码,用于验证访问者的身份。

ecedee247972ca9cb71e9d6de9fe76a0.jpg

🌽 MinIO客户端实操


🥬 创建bucket


这里的bucket存储桶是用于组织对象的名称空间,类似于我们所说的文件夹。




🥜 测试文件上传


然后来测试一下,文件上传。



上传文件,点击"upload",选择上传的文件即可。



🥖 设置匿名用户的访问权限



将匿名用户权限设置为只读。



🧆 创建 Access Key


这里的Access Key用于标识和验证访问者身份的唯一标识符,相当于用户名。




如上操作完后,我们便来进行此期的真正的干货了,直接上手实操。


69213999f99c5149c19b1d5d73479aee.jpg

🌯 Spring Boot集成MinIO的实操指南


🫔 环境准备


  首先,确保你的开发环境已经配置好以下工具:



  • JDK 1.8
  • Spring Boot 2.6+
  • MinIO服务(可使用Docker快速部署)

docker run -p 9000:9000 -p 9001:9001 --name minio \  
-e "MINIO_ROOT_USER=admin" \
-e "MINIO_ROOT_PASSWORD=password123" \
minio/minio server /data --console-address ":9001"

  这段命令会在本地启动MinIO服务,你只需要打开浏览器,输入http://localhost:9001,用设置的账号密码登录,即可看到管理界面。


  或者你也可以参考Linux常规搭建,可看这篇《Linux零基础安装Minio,手把手教学,一文搞定它!(超详细)》,妥妥傻瓜式教学。


🫑 引入依赖


  接下来,修改pom.xml,引入MinIO的Java SDK依赖:


        <!--minio oss服务-->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.12</version>
</dependency>

🍌 定义MinIO连接信息


  我们需要先将minio的连接信息配置到我们的配置类中,方便修改及动态配置。


  故我们需要先去minio的客户端先创建于一个access key,然后将access-key 与 secret-key 填写到 yml 配置文件中。



具体配置如下,你们直接改成你们的即可。


# minio文件存储
minio:
access-key: Ro2ypdSShhmqQYgHWyDP
secret-key: 6XOaQsYXBKflV10KDcjgcwE9lvekcN4KYfE85fBL
url: http://10.66.66.143:9000
bucket-name: hpy-files

属性解读:


如上这段代码配置的是MinIO文件存储的连接信息,具体内容如下:



  • access-key: Ro2ypdSShhmqQYgHWyDP — 这是MinIO的访问密钥(类似于用户名),用于身份验证。
  • secret-key: 6XOaQsYXBKflV10KDcjgcwE9lvekcN4KYfE85fBL — 这是MinIO的密钥(类似于密码),用于进行身份验证。
  • url: http://10.66.66.143:9000 — 这是MinIO服务器的地址,表示文件存储服务的主机IP地址和端口。
  • bucket-name: hpy-files — 这是用于存储文件的桶(bucket)名称。在MinIO中,文件是按桶来存储和组织的。

5fa807cad56d9e7ac57fe8eb235f2fa4.jpg

🍐 配置MinIO客户端


  我们需要为Spring Boot项目配置一个MinIO客户端。新建MinioConfig.java


/**
* @author: bug菌
* @date: 2024-10-21 11:59
*/

@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {
private String accessKey;

private String secretKey;

private String url;

private String bucketName;

@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.region("cn-north-1")
.endpoint(url)
.credentials(accessKey, secretKey)
.build();
}
}

  配置完成后,MinIO客户端就已经准备好为我们的Spring Boot项目服务了。


266e9e90ecde3e912cee32e7527d6107.jpg

🍌 创建文件工具类


  接下来,我们需要创建一个MinioUtil类,该类的目的是为了封装和简化与 MinIO 文件存储服务的交互,提供一系列的操作方法,使得我们能够轻松地进行文件上传、下载、删除、获取文件信息等常见的文件存储操作。具体意义如下:



  1. 与 MinIO 交互的封装
    类中封装了与 MinIO 存储服务进行交互的代码,包括检查存储桶是否存在、文件上传、下载、删除等常见的操作。这样,业务逻辑代码无需直接操作 MinIO API,提升了代码的复用性和可维护性。
  2. 自动化存储桶管理
    @PostConstruct 注解的 init() 方法中,会自动检查并创建存储桶(bucket)。确保在程序启动时,指定的存储桶已经存在,避免了在使用过程中因存储桶不存在而导致的错误。
  3. 支持文件的 URL 生成
    提供了生成文件访问 URL 的功能,包括带过期时间的预签名 URL。这是为了允许用户在一定时间内访问文件,避免文件暴露或在外部用户访问时需要额外认证。
  4. 文件下载支持
    类中提供了文件下载的功能,包括标准下载(通过 HTTP ServletResponse)和流式下载(获取文件流)。它可以处理文件的大小、编码等问题,保证文件的正确下载。
  5. 文件操作的错误处理与日志
    通过 Logger 对操作进行记录,且所有可能抛出异常的操作都进行了捕获和处理,避免了程序因为 MinIO 服务故障等原因而直接崩溃。确保系统的稳定性和错误反馈。
  6. 文件夹与文件的存在性检查
    该类提供了检查文件或文件夹是否存在的方法,有助于在上传或删除文件前进行状态验证,避免重复操作。
  7. 简化 API 调用
    通过抽象出一层高层次的操作接口,开发者不需要直接关注 MinIO 底层的复杂实现,只需调用简洁的方法即可完成文件存储操作。

  总结而言,MinioUtil 类通过封装 MinIO 的常见文件操作,提供便捷的接口,降低与 MinIO 交互的复杂性,并通过统一的错误处理和日志记录,增强了系统的健壮性和可维护性。


dee10a6f4baf10fd658a0431bc2c4222.jpg

代码实操:


/**
* 文件工具类
*
* @author: bug菌
* @date: 2024-10-21 12:02
* @desc:
*/

@Service
public class MinioUtil {
private static final Logger log = LoggerFactory.getLogger(MinioUtil.class);

@Autowired
private MinioClient minioClient;
@Autowired
private MinioConfig minioConfig;

@PostConstruct
public void init() {
existBucket(minioConfig.getBucketName());
}


/**
* 判断bucket是否存在,不存在则创建
*/

public boolean existBucket(String bucketName) {
boolean exists;
try {
exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
if (!exists) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
exists = true;
}
} catch (Exception e) {
e.printStackTrace();
exists = false;
}
return exists;
}

/**
* 上传文件
*/

public void upload(MultipartFile file, String fileName) {
// 使用putObject上传一个文件到存储桶中。
InputStream inputStream = null;
try {
inputStream = file.getInputStream();
minioClient.putObject(PutObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(fileName)
.stream(inputStream, file.getSize(), -1)
.contentType(file.getContentType())
.build());
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}

/**
* 获取文件访问地址
*/

public String getFileUrl(String fileName) {
try {
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(minioConfig.getBucketName())
.object(fileName)
.build()
);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}

/**
* 下载一个文件(返回文件流)
*/

public InputStream download(String objectName) throws Exception {
InputStream stream = minioClient.getObject(
GetObjectArgs.builder().bucket(minioConfig.getBucketName()).object(objectName).build());
return stream;
}


/**
* 下载文件
*/

public void download(HttpServletResponse response, String newFileName, String saveFileName) {
InputStream in = null;
try {
// 获取对象信息
StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(saveFileName)
.build());

// 设置请求头Content-Type
response.setContentType(stat.contentType());

// 确保使用 UTF-8 编码
// String encodedFileName = encodeFilename(newFileName);
String encodedFileName = URLEncoder.encode(newFileName, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFileName + "\"");

// 设置禁用缓存
response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
response.setHeader("Pragma", "no-cache");
response.setHeader("Expires", "0");

// 设置文件大小
long fileSize = stat.size();
response.setContentLengthLong(fileSize);

// 获取文件输入流
in = minioClient.getObject(GetObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(saveFileName)
.build());

// 文件下载
IOUtils.copy(in, response.getOutputStream());

} catch (Exception e) {
e.printStackTrace();
try {
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "File download failed: " + e.getMessage());
} catch (IOException ioException) {
ioException.printStackTrace();
}
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}


/**
* 删除文件
*/

public void delete(String fileName) {
try {
minioClient.removeObject(RemoveObjectArgs.builder().bucket(minioConfig.getBucketName()).object(fileName).build());
} catch (Exception e) {
e.printStackTrace();
}
}


/**
* 判断文件是否存在
*
* @param objectName
*/

public boolean isFileExist(String objectName) {
boolean exist = true;
try {
minioClient.statObject(StatObjectArgs.builder().bucket(minioConfig.getBucketName()).object(objectName).build());
} catch (Exception e) {
log.error("[Minio工具类]>>>> 判断文件是否存在, 异常:", e);
exist = false;
}
return exist;
}
}

7f6fe9df118f89fc095c1d2cb38de919.jpg

📝 文件上传/下载/预览/删除实战


🧁 1.文件上传


🍆 示例代码


/**
* @author: bug菌
* @date: 2024-10-21 12:07
*/

@Api(tags = "Minio文件管理")
@RestController
@RequestMapping("/file")
public class UploadFileController extends BaseController {

@Autowired
private MinioUtil minioUtil;

/**
* 上传文件
*/

@GetMapping(value = "/upload")
@ApiOperation("上传文件")
public R upload(MultipartFile file) {
// 获取到上传文件的完整名称,包括文件后缀
String fileName = file.getOriginalFilename();
// 获取不带后缀的文件名
String baseName = FilenameUtils.getBaseName(fileName);
// 获取文件后缀
String extension = FilenameUtils.getExtension(fileName);
//创建一个独一的文件名(存于服务器名),格式为 name_时间戳.后缀
String saveFileName = baseName + "_" + System.currentTimeMillis() + "." + extension;
minioUtil.upload(file, saveFileName);
return R.ok("上传成功!存放文件名为:" + saveFileName);
}
}

🥔 示例测试


Postman接口测试上传接口如下:



校验文件是否真正上传到minio中,我们可以上客户端查验下。根据登录查看确实是我们测试时所上传的文件。



🍓 示例代码解析


  在本次的代码演示中,我将会深入剖析每句代码,详细阐述其背后的设计思想和实现逻辑。通过这样的讲解方式,我希望能够引导同学们逐步构建起对代码的深刻理解。我会先从代码的结构开始,逐步拆解每个模块的功能和作用,并指出关键的代码段,并解释它们是如何协同运行的。通过这样的讲解和实践相结合的方式,我相信每位同学都能够对代码有更深入的理解,并能够早日将其掌握,应用到自己的学习和工作中。


  如上提供的这段代码是一个用于文件上传的控制器,使用 Spring Boot 构建,负责处理文件的上传操作。以下是代码的详细解析:


1a5f7060d8c39e9f636e750a9d474152.jpg


  1. 类注解

    • @Api(tags = "Minio文件管理"):使用 Swagger API 文档工具生成接口文档,并为该类提供了一个标签“Minio文件管理”,用于描述文件管理相关的接口。
    • @RestController:该注解表示这是一个控制器类,并且返回的内容会被自动序列化为 JSON 格式。它是 @Controller@ResponseBody 的组合。
    • @RequestMapping("/file"):设置该类的基础请求路径为 /file,所有该类中的请求都会以 /file 开头。


  2. 依赖注入

    • @Autowired:自动注入 MinioUtil 类的实例,MinioUtil 是一个封装了 MinIO 操作的工具类,用于处理与 MinIO 存储服务的交互。


  3. 方法注解

    • @GetMapping(value = "/upload"):处理 HTTP GET 请求,路径为 /file/upload。尽管通常文件上传使用 POST 请求,但这里使用 GET 请求可能是简化了请求示例,实际应用中可能使用 POST。
    • @ApiOperation("上传文件"):Swagger 文档生成的描述,表示该接口用于上传文件。


  4. 上传文件操作

    • MultipartFile file:表示前端传递的文件。Spring 会自动将请求中的文件映射到该参数。
    • String fileName = file.getOriginalFilename();:获取上传文件的原始文件名,包括文件扩展名。
    • String baseName = FilenameUtils.getBaseName(fileName);:使用 Apache Commons IO 库的 FilenameUtils 类,获取文件的基本名称(不包含扩展名)。
    • String extension = FilenameUtils.getExtension(fileName);:获取文件的扩展名。
    • String saveFileName = baseName + "_" + System.currentTimeMillis() + "." + extension;:生成一个新的唯一文件名。通过文件的基本名称加上当前的时间戳(毫秒级),确保文件名不重复。
    • minioUtil.upload(file, saveFileName);:调用 MinioUtil 类中的 upload 方法,将文件上传到 MinIO 存储服务,保存为 saveFileName


  5. 返回结果

    • return R.ok("上传成功!存放文件名为:" + saveFileName);:返回上传成功的响应,R.ok() 是一个自定义的响应方法,表示操作成功并返回相应的信息,saveFileName 作为返回信息的一部分,告知客户端上传文件后的存储文件名。



小结:


  该控制器类用于处理文件上传请求,接收文件并生成一个唯一的文件名,通过 MinioUtil 工具类将文件上传至 MinIO 存储。它结合了文件名生成、上传及响应返回等功能,实现了简单的文件上传管理。


70861610ce5f1a5dfab62152b6a95fcf.jpg

🍬 2.文件下载


🍆 示例代码


    /**
* 根据文件ID下载文件
*/

@GetMapping("/download")
@ApiOperation("根据文件ID下载文件")
public void downloadById(@RequestParam("fileName") String fileName, @RequestParam("saveFileName") String saveFileName, HttpServletResponse response) {
// 下载文件,传递存储文件名和显示文件名
minioUtil.download(response, fileName, saveFileName);
return;
}

🥔 示例测试


Postman接口测试上传接口如下:




🍓 示例代码解析


  在本次的代码演示中,我将会深入剖析每句代码,详细阐述其背后的设计思想和实现逻辑。通过这样的讲解方式,我希望能够引导同学们逐步构建起对代码的深刻理解。我会先从代码的结构开始,逐步拆解每个模块的功能和作用,并指出关键的代码段,并解释它们是如何协同运行的。通过这样的讲解和实践相结合的方式,我相信每位同学都能够对代码有更深入的理解,并能够早日将其掌握,应用到自己的学习和工作中。


  如上提供的这段代码是用于根据文件ID下载文件的控制器方法。以下是对代码的详细解析:



  1. 方法注解

    • @GetMapping("/download"):该方法处理 HTTP GET 请求,路径为 /download。该请求用于根据文件ID下载文件。
    • @ApiOperation("根据文件ID下载文件"):Swagger 文档生成的描述,表明该接口用于根据文件ID下载文件。


  2. 方法参数

    • @RequestParam("fileName") String fileName:从请求中获取名为 fileName 的请求参数,并将其绑定到 fileName 变量。这个参数通常表示文件在存储中的实际名称。
    • @RequestParam("fileName") String saveFileName:这个参数也是从请求中获取名为 fileName 的请求参数。由于参数名称重复,可能会导致问题。正确的做法是使用不同的名字,例如 fileNamesaveFileName,用来分别传递存储文件名和显示文件名。
    • HttpServletResponse response:Spring MVC 自动注入的 HttpServletResponse 对象,用于设置响应信息,发送文件内容到客户端。


  3. 下载文件操作

    • minioUtil.download(response, fileName, saveFileName);:调用 MinioUtil 类中的 download 方法。该方法接收 HttpServletResponse 对象、存储文件名(fileName)和显示文件名(saveFileName)作为参数。download 方法将从 MinIO 存储中获取指定的文件并通过 HTTP 响应将其返回给客户端。


  4. 方法结束

    • return;:该方法没有返回任何内容,因为文件内容通过 HttpServletResponse 被直接流式传输到客户端。



小结:


  该方法用于处理根据文件ID下载文件的请求。它通过传递文件名参数,调用 MinioUtil 的下载方法,将文件从 MinIO 存储下载并返回给客户端。


3e27e9994c1c564bfa0e989734bb229f.jpg

🍩 3.文件预览


🍓 示例代码


    @GetMapping("/preview")
@ApiOperation("根据文件ID预览文件")
public String previewFileById(@RequestParam("fileName") String fileName) {
return minioUtil.getFileUrl(fileName);
}

🥔 示例测试


Postman接口测试上传接口如下:



  通过接口可直接给你返回该文件的预览地址,我们只需要在浏览器输入该地址便可预览。


🍆 示例代码解析


  在本次的代码演示中,我将会深入剖析每句代码,详细阐述其背后的设计思想和实现逻辑。通过这样的讲解方式,我希望能够引导同学们逐步构建起对代码的深刻理解。我会先从代码的结构开始,逐步拆解每个模块的功能和作用,并指出关键的代码段,并解释它们是如何协同运行的。通过这样的讲解和实践相结合的方式,我相信每位同学都能够对代码有更深入的理解,并能够早日将其掌握,应用到自己的学习和工作中。


  如上提供的这段代码是用于根据文件ID预览文件的控制器方法。以下是详细解析:



  1. 方法注解

    • @GetMapping("/preview"):该方法处理 HTTP GET 请求,路径为 /preview,用于根据文件ID预览文件。
    • @ApiOperation("根据文件ID预览文件"):Swagger 文档生成的描述,表明该接口用于根据文件ID预览文件。


  2. 方法参数

    • @RequestParam("fileName") String fileName:从请求中获取名为 fileName 的请求参数,并将其绑定到 fileName 变量。这个参数通常表示要预览的文件在存储中的文件名。


  3. 文件预览操作

    • minioUtil.getFileUrl(fileName):调用 MinioUtil 类中的 getFileUrl 方法,该方法使用文件名从 MinIO 存储生成文件的预览 URL。返回的 URL 通常是一个可以直接访问该文件的链接,可以在客户端浏览器中打开进行预览。


  4. 返回值

    • 方法返回 String 类型的文件预览 URL,这个 URL 可以直接访问文件并在浏览器中预览。



小结:


  该方法用于处理根据文件ID预览文件的请求。它通过文件名生成一个文件的预览 URL,并将该 URL 返回给客户端,客户端可以使用该 URL 访问文件进行预览。


594fb81379a7967c609738734d94948f.jpg

🍭 4.文件删除


🍓 示例代码


    /**
* 根据文件ID删除文件
*/

@GetMapping("/delete")
@ApiOperation("根据文件ID删除文件")
public R deleteById(@RequestParam("fileName") String fileName) {
minioUtil.delete(fileName);
return R.ok();
}

🥔 示例测试


Postman接口测试上传接口如下:



接着我们上客户端查验下,该文件是否真被删除了。



根据时间倒序排序,确实该文件被删除了。


🍆 示例代码解析


  在本次的代码演示中,我将会深入剖析每句代码,详细阐述其背后的设计思想和实现逻辑。通过这样的讲解方式,我希望能够引导同学们逐步构建起对代码的深刻理解。我会先从代码的结构开始,逐步拆解每个模块的功能和作用,并指出关键的代码段,并解释它们是如何协同运行的。通过这样的讲解和实践相结合的方式,我相信每位同学都能够对代码有更深入的理解,并能够早日将其掌握,应用到自己的学习和工作中。


  如上提供的这段代码是用于根据文件ID删除文件的控制器方法。以下是详细解析:



  1. 方法注解

    • @GetMapping("/delete"):该方法处理 HTTP GET 请求,路径为 /delete,用于根据文件ID删除文件。
    • @ApiOperation("根据文件ID删除文件"):Swagger 文档生成的描述,表明该接口用于根据文件ID删除文件。


  2. 方法参数

    • @RequestParam("fileName") String fileName:从请求中获取名为 fileName 的请求参数,并将其绑定到 fileName 变量。这个参数通常表示要删除的文件在存储中的文件名。


  3. 删除文件操作

    • minioUtil.delete(fileName):调用 MinioUtil 类中的 delete 方法,该方法会根据提供的 fileName 删除 MinIO 存储中的对应文件。


  4. 返回值

    • 方法返回 R.ok():表示操作成功,返回一个响应对象,R.ok() 是一种常见的封装返回成功的方式,可能会带有自定义的状态码或消息。



小结:
  该方法处理根据文件ID删除文件的请求。它通过文件名调用 MinioUtil 删除对应的文件,并返回一个成功的响应。


f9afddb15d8b64f1ed00592005998c8d.jpg

🫐 MinIO与云原生架构的完美契合


  MinIO不仅是一个存储工具,它更是云原生架构中不可或缺的一部分。与Kubernetes无缝整合,让微服务架构下的数据管理变得轻松自如。不论是CI/CD流水线还是大数据分析,MinIO都能应对自如。


🍐 总结与思考


  通过这篇文章,你应该对Spring Boot与MinIO的结合有了一个全面的了解。这种现代化的文件存储方案不仅让开发更高效,也为未来业务的扩展奠定了坚实基础。既然已经Get到这么棒的技能,何不立即尝试一下,让你的项目也能“飞”起来?


fb4f7fce18746726751c26e1b136acbc.jpg

🥕 附录相关报错及方案解决


🫛1、okhttp3包冲突


  如果你遇到你的项目集成 minio 8.5.4 遇到 okhttp3包冲突,比如报错如下所示,可见我这篇《SpringBoot项目集成 minio 8.5.4 遇到 okhttp3包冲突,如何解决?》带你解决此问题:



🍏2、启动报错


  如果你启动后遇到如下问题,比如报错如下所示,可见我这篇《集成minio启动报错:Caused by:java.lang.IllegalArgumentException:invalid hostname 10.66.66.143:9000...| 亲测有效》带你解决此问题:



  ok,本期内容我就暂聊到这里,哇,一口气给大家输出完,我我我我...头发又脱落了一撮。


205e5cd4340a69eddf9d57be0ec35c92.jpg

📣 关于我


我是bug菌,CSDN | 掘金 | InfoQ | 51CTO | 华为云 | 阿里云 | 腾讯云 等社区博客专家,C站博客之星Top30,华为云2023年度十佳博主,掘金多年度人气作者Top40,掘金等各大社区平台签约作者,51CTO年度博主Top12,掘金/InfoQ/51CTO等社区优质创作者;全网粉丝合计 30w+;硬核微信公众号「猿圈奇妙屋」,欢迎你的加入!免费白嫖最新BAT互联网公司面试真题、4000G PDF电子书籍、简历模板等海量资料,你想要的我都有,关键是你不来拿哇。





-End-

作者:bug菌
来源:juejin.cn/post/7443658338867134518

0 个评论

要回复文章请先登录注册