Mall脚手架总结(五) —— SpringBoot整合MinIO实现文件管理

news2025/4/13 6:55:43

前言

        在项目中我们经常有资源的上传和下载的功能需求,比如用户头像、产品图片、配置文件等等,大数据量的文件存储无疑需要更高性能的数据存储服务,对于无需对结构实现复杂查询的文件对象来说,对象存储服务无疑是一个较好的选择,在下面的文章中荔枝也会梳理脚手架整合MinIO实现文件管理。希望能帮助到有需要的小伙伴~~~


文章目录

前言

一、MinIO实现文件管理

1.1 为什么使用对象存储服务

1.1.1 OSS与传统的文件系统的区别

1.1.2 OSS与非关系型数据库的用法区分

1.2 CorsFilter

1.2.1 跨源资源共享

1.2.2 CorsFilter剖析

1.3 @RequestPart注解

MultipartFile和File

1.4 MinioClient

1.5 Minio的访问策略

总结


一、MinIO实现文件管理

1.1 为什么使用对象存储服务

        在项目中,往往有些数据比如头像、资源文件等,这些数据的数据量往往比较大但又不需要执行频繁的数据操作,也没有复杂的结构关系,这些数据的存储我们往往尝试选择更好的存储方式。就拿图片来说吧,存在MySQL这种关系型数据库明显不是很合适,因为它是块存储的,要先将文件编码成二进制文本才能存进去,同时关系型数据库中会增加大量的数据比如索引、字段、日志等,查询效率明显差强人意。这时候我们需要一个能够直接存储整个文件对象的工具,这时我们就要用OSS(对象存储服务)。 

1.1.1 OSS与传统的文件系统的区别

        相比于传统的文件管理系统,对象存储服务提供了一种更简单、更灵活的数据源存储方式,无需我们预先定义文件系统的目录结构,而是将文件作为一个对象来存储,文件数据的管理和检索更加方便。在对象存储服务中,每个对象都有一个唯一的标识符(通常是一个特定的URL)

1.1.2 OSS与非关系型数据库的用法区分

        小伙伴们可能跟荔枝一样,想起图像、文件这些数据是不是也可以存在非关系型数据库里面比如MongoDB。是的的确可以,但我们要弄清楚要存储文件的操作情况,根据OSS和非关系型数据库的应用场景来选择适合需求的存储工具。

  • 对象存储服务: 适用于大规模的、静态的文件存储需求,例如图片、音频、视频文件等。对象存储通常具有较高的可扩展性和稳定性。
  • 非关系型数据库: 适用于需要对存储的文件进行复杂查询、分析和变换的场景,例如社交网络应用、日志分析等。

总结一下,之所以选择MinIO是因为其在无需过多操作的文件数据对象的存储上表现更优异,但是注意的是MinIO不是数据库,而是一种数据对象存储服务! 

云存储和云数据库区别:MinIO与MySQL对比以及存储的相关知识_minio数据库_Fishermen_sail的博客-CSDN博客  

1.2 CorsFilter

在正式梳理CorsFilter之前我们需要了解什么是CROS

1.2.1 跨源资源共享

        CORS(Cross-origin resource sharing)跨源资源共享,是一种用于在浏览器和服务器之间进行安全跨域数据传输的机制。在 Web 开发中,CORS 允许网页从不同的域(协议、域名、端口)请求受限制资源,而不受同源策略的限制。正常情况下,浏览器是会阻止跨域请求来加载自身的资源,而CORS 通过在服务器端设置响应头的方式,允许服务器声明哪些源可以访问其资源,从而绕过了同源策略的限制。

浏览器将CORS请求分为两类:简单请求和非简单请求。

  • 简单请求:浏览器会自动在请求头中增加一个Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)之后服务器再根据这个值,决定是否同意这次请求;
  • 非简单请求:浏览器会自动发送一个请求方法为Option的预检请求,里面也包含着Origin字段、Access-Control-Request-Method(列出浏览器的CORS请求会用到哪些HTTP方法)以及Access-Control-Request-Headers(指定浏览器CORS请求会额外发送的头信息字段)。

更为详细的内容参见博文:

 http://www.ruanyifeng.com/blog/2016/04/cors.html

https://blog.csdn.net/zzuhkp/article/details/120631687

1.2.2 CorsFilter剖析

        CorsFilter是一个用于处理跨域资源共享(CORS)的过滤器。在SpringBoot中整合MinIO实现文件管理的时候我们需要自定义一个配置类来允许跨域访问,需要通过Spring中web框架中的CorsFilter来封装我们的配置信息。

 完整的类继承关系如下:

简单看一下这个类的源码:

public class CorsFilter extends OncePerRequestFilter {
    private final CorsConfigurationSource configSource;
    private CorsProcessor processor = new DefaultCorsProcessor();

    public CorsFilter(CorsConfigurationSource configSource) {
        Assert.notNull(configSource, "CorsConfigurationSource must not be null");
        this.configSource = configSource;
    }

    public void setCorsProcessor(CorsProcessor processor) {
        Assert.notNull(processor, "CorsProcessor must not be null");
        this.processor = processor;
    }

    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        //获取配置信息
        CorsConfiguration corsConfiguration = this.configSource.getCorsConfiguration(request);
        //处理请求
        boolean isValid = this.processor.processRequest(corsConfiguration, request, response);
        if (isValid && !CorsUtils.isPreFlightRequest(request)) {
            //过滤器链过滤
            filterChain.doFilter(request, response);
        }
    }
}
  • CorsConfigurationSource对象:代表了当前请求的 CORS 配置。
  • CorsProcessor类:负责处理请求,并根据 CORS 配置信息判断是否允许该请求。
  • doFilterInternal 方法:该方法CorsFilter的父类 OncePerRequestFilter 类中的一个抽象方法,需要在子类中实现。在这个方法中,首先从configSource获取当前请求的 CORS 配置信息。然后,通过CorsProcessor对象(默认使用DefaultCorsProcessor类)处理当前请求。
  • processRequest 方法:processRequest 方法是 CorsProcessor 接口中的一个方法,用于处理请求。具体的 CORS 处理逻辑在该方法中实现。
  • filterChain.doFilter 方法:如果请求是有效的且不是预检请求(Preflight Request),则调用 filterChain.doFilter(request, response)继续处理请求链。

在SpringBoot项目中我们仅需要通过一个配置类向其传入配置信息即可,如下面的示例代码,UrlBasedCorsConfigurationSource是CorsConfigurationSource接口的一个实现类。

@Configuration
public class GlobalCorsConfig {
    /**
     * 允许跨域调用的过滤器
     */
    @Bean
    public CorsFilter corsFilter() {
        //配置信息
        CorsConfiguration config = new CorsConfiguration();
        //允许所有域名进行跨域调用
        config.addAllowedOriginPattern("*");
        //允许跨越发送cookie
        config.setAllowCredentials(true);
        //放行全部原始头信息
        config.addAllowedHeader("*");
        //允许所有请求方法跨域调用
        config.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

区分CorsConfiguration 和CorsConfigurationSource 

  • CorsConfiguration类:用于配置CORS规则。你可以在这个对象上设置允许的来源、允许的HTTP方法、允许的请求头等
  • CorsConfigurationSource接口:用于提供CorsConfiguration对象,通过自定义实现类可以根据请求的不同,为不同的资源提供不同的CORS配置。

1.3 @RequestPart注解

前端中我们上传数据到服务器中通常在form表单中用一个enctype标记为multipart/form-dataPOST请求来实现,在input表单中声明type为file即可实现文件上传。

<form action="http://127.0.0.1" method="post" enctype="multipart/form-data">
    <input type="file" name="file1" value="请选择文件"/><br/>
    <input type="submit"/>
</form>

而在Spring中我们可以通过@RequestPart注解来获取表单上传的数据。该注解是Spring 框架中用于处理multipart/form-data请求中的文件上传的注解。

@ApiOperation("文件上传")
@RequestMapping(value = "/upload", method = RequestMethod.POST)
@ResponseBody
public CommonResult upload(@RequestPart("file") MultipartFile file) 

MultipartFile和File

这一部分大家可以看看荔枝梳理的文章嘿~

彻底弄懂Java中的MultipartFile接口和File类_荔枝当大佬的博客-CSDN博客

1.4 MinioClient

该类是Java操作MinIO客户端的工具库,用于与MinIO对象存储服务进行交互。

创建MinIO客户端的操作

//创建一个MinIO的Java客户端
MinioClient minioClient =MinioClient.builder()
                    .endpoint(访问地址)
                    .credentials(登陆令牌,登录密钥)
                    .build();

文件上传操作代码

@Controller
@Api(tags = "MinioController")
@Tag(name = "MinioController", description = "MinIO对象存储管理")
@RequestMapping("/minio")
public class MinioController {

    private static final Logger LOGGER = LoggerFactory.getLogger(MinioController.class);
    @Value("${minio.endpoint}")
    private String ENDPOINT;
    @Value("${minio.bucketName}")
    private String BUCKET_NAME;
    @Value("${minio.accessKey}")
    private String ACCESS_KEY;
    @Value("${minio.secretKey}")
    private String SECRET_KEY;

    @ApiOperation("文件上传")
    @RequestMapping(value = "/upload", method = RequestMethod.POST)
    @ResponseBody
    public CommonResult upload(@RequestPart("file") MultipartFile file) {
        try {
            //创建一个MinIO的Java客户端
            MinioClient minioClient =MinioClient.builder()
                    .endpoint(ENDPOINT)
                    .credentials(ACCESS_KEY,SECRET_KEY)
                    .build();
            //检查桶是否存在
            boolean isExist = minioClient.bucketExists(BucketExistsArgs.builder().bucket(BUCKET_NAME).build());
            if (isExist) {
                LOGGER.info("存储桶已经存在!");
            } else {
                //创建存储桶并设置只读权限
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(BUCKET_NAME).build());
                BucketPolicyConfigDto bucketPolicyConfigDto = createBucketPolicyConfigDto(BUCKET_NAME);
                SetBucketPolicyArgs setBucketPolicyArgs = SetBucketPolicyArgs.builder()
                        .bucket(BUCKET_NAME)
                        .config(JSONUtil.toJsonStr(bucketPolicyConfigDto))
                        .build();
                //将策略配置应用到指定的 MinIO 存储桶中
                minioClient.setBucketPolicy(setBucketPolicyArgs);
            }
            String filename = file.getOriginalFilename();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
            // 设置存储对象名称
            String objectName = sdf.format(new Date()) + "/" + filename;
            // 使用putObject上传一个文件到存储桶中
            PutObjectArgs putObjectArgs = PutObjectArgs.builder()
                    .bucket(BUCKET_NAME)
                    .object(objectName)
                    .contentType(file.getContentType())
                    .stream(file.getInputStream(), file.getSize(), ObjectWriteArgs.MIN_MULTIPART_SIZE).build();
            minioClient.putObject(putObjectArgs);
            LOGGER.info("文件上传成功!");
            MinioUploadDto minioUploadDto = new MinioUploadDto();
            minioUploadDto.setName(filename);
            minioUploadDto.setUrl(ENDPOINT + "/" + BUCKET_NAME + "/" + objectName);
            return CommonResult.success(minioUploadDto);
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.info("上传发生错误: {}!", e.getMessage());
        }
        return CommonResult.failed();
    }
}

这里的 BucketPolicyConfigDto类是自定义的访问策略类。

@Data
@EqualsAndHashCode
@Builder
public class BucketPolicyConfigDto {
    private String Version;
    private List<Statement> Statement;

    @Data
    @EqualsAndHashCode
    @Builder
    public static class Statement {
        private String Effect;
        private String Principal;
        private String Action;
        private String Resource;
    }
}

删除文件操作

MinioClient minioClient = MinioClient.builder()
                    .endpoint(ENDPOINT)
                    .credentials(ACCESS_KEY,SECRET_KEY)
                    .build();
minioClient.removeObject(RemoveObjectArgs.builder().bucket(BUCKET_NAME).object(objectName).build());

1.5 Minio的访问策略

MinIO中的访问策略主要有三种:public、private和consume(自定义)

  • Version: 策略的版本,目前的版本为 "2012-10-17"。
  • Statement: 一个数组,包含了访问规则的定义。

每个 Statement 包含以下属性:

  • Effect: 指定策略的效果,可以是 "Allow"(允许)或 "Deny"(拒绝)。
  • Action: 指定允许或拒绝的操作,可以是一个字符串或一个字符串数组,例如 "s3:GetObject" 或 ["s3:GetObject", "s3:PutObject"]。
  • Resource: 指定操作作用的资源,可以是一个字符串或一个字符串数组,例如 "arn:aws:s3:::my-bucket/*"。
  • Principal: 指定被授权的实体,可以是 "*"(表示所有用户)或特定的用户或角色。

一个简单的访问策略 

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": [
                    "*"
                ]
            },
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::mall/*.**"
            ]
        }
    ]
}

总结

        终章!荔枝把脚手架中的知识内容梳理了一遍,也确实学到了之前没有了解过的类和一些工具接口,对项目中用到的中间件的使用体会也更加深入了。有些类的源码确实太多了哈哈哈,根据自己的需求荔枝确实在有选择地阅读和理解,同时也感谢宏哥的帮助和许多博主的博文浇灌哈哈哈哈,继续加油!

今朝已然成为过去,明日依然向往未来!我是荔枝,在技术成长之路上与您相伴~~~

如果博文对您有帮助的话,可以给荔枝一键三连嘿,您的支持和鼓励是荔枝最大的动力!

如果博文内容有误,也欢迎各位大佬在下方评论区批评指正!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1094274.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

并查集路径压缩

我们来看看如果要是100个数&#xff0c;往20个数的集合合并如何 那么我们应该&#xff0c;把数据量小的集合往数据量大的合并 解决另一种极端场景的路径压缩

限制条件加入构造范围:Gym - 102832L

https://vjudge.net/contest/587311#problem/D 场上列方程求首项&#xff0c;假设是全部加1&#xff0c;然后一部分&#xff08;后缀&#xff09;减去 k 1 k1 k1&#xff0c;就用到了以下两个条件&#xff1a; 但在这两种情况符合情况下&#xff0c;这个条件不一定满足 然后…

【软件测试】 初识软件测试

文章目录 &#x1f334;什么是软件测试&#x1f38b;软件测试和开发的区别&#x1f6a9;软件测试与调试的区别 &#x1f333;软件测试的发展&#x1f384;软件测试岗位&#x1f340;软件测试在不同类型公司的定位&#x1f38d;一个优秀的软件测试人员具备的素质&#x1f332;软…

105AspectRatio调整宽高比组件_flutter

AspectRatio组件 AspectRatio 的作用是根据设置调整子元素 child 的宽高比。 AspectRatio 首先会在布局限制条件允许的范围内尽可能的扩展&#xff0c;widget 的高度是由宽 度和比率决定的&#xff0c;类似于 BoxFit 中的 contain&#xff0c;按照固定比率去尽量占满区域。 …

【Redis】Java Spring操作redis

目录 引入Redis依赖StringRedisTemplate使用String使用List使用Set使用hash使用zset 引入Redis依赖 StringRedisTemplate 此处RedisTemplate是把这些操作Redis的方法&#xff0c;分成了几个类别&#xff0c;分门别类的来组织的。 此处提供的一些接口风格&#xff0c;和原生的Re…

利达卓越:发挥金融力量,促进团队发展

随着中国经济的快速增长和金融改革的逐步深化&#xff0c;我国金融业取得了令人瞩目的发展。作为经济的重要支柱&#xff0c;我国金融业的规模和实力不断扩大&#xff0c;已经成为全球最大的金融市场之一。利达卓越是一支由管理精英组成的团队&#xff0c;专注于金融行业的投资…

Empowering Low-Light Image Enhancer through Customized Learnable Priors 论文阅读笔记

中科大、西安交大、南开大学发表在ICCV2023的论文&#xff0c;作者里有李重仪老师和中科大的Jie Huang&#xff08;ECCV2022的FEC CVPR2022的ENC和CVPR2023的ERL的一作&#xff09;喔&#xff0c;看来可能是和Jie Huang同一个课题组的&#xff0c;而且同样代码是开源的&#xf…

解密人工智能:KNN | K-均值 | 降维算法 | 梯度Boosting算法 | AdaBoosting算法

文章目录 一、机器学习算法简介1.1 机器学习算法包含的两个步骤1.2 机器学习算法的分类 二、KNN三、K-均值四、降维算法五、梯度Boosting算法和AdaBoosting算法六、结语 一、机器学习算法简介 机器学习算法是一种基于数据和经验的算法&#xff0c;通过对大量数据的学习和分析&…

数据结构与算法-(8)---队列(Queue)

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

若依 ruoyi 路径 地址 # 井号去除

export default new Router({mode: history, // history 去掉url中的# 、hash 包含#号scrollBehavior: () > ({ y: 0 }),routes: constantRoutes })

嘉立创专业版新建元件

以2*24&#xff08;2mm&#xff09;排母为例 文件-新建-元件 新建器件 填上元件的基本信息&#xff0c;保存 选择库设计 填好参数&#xff0c;生成符号 给元件添加封装 需要先设计一个封装 选择header-V 填写参数&#xff0c;生成符号 保存即可。 再次进入元件的封装管理…

207、SpringBoot 整合 RabbitMQ 实现消息的发送 与 接收(监听器)

目录 ★ 发送消息★ 创建队列的两种方式代码演示需求1&#xff1a;发送消息1、ContentUtil 先定义常量2、RabbitMQConfig 创建队列的两种方式之一&#xff1a;配置式&#xff1a;问题&#xff1a; 3、MessageService 编写逻辑PublishController 控制器application.properties 配…

抖音开放平台第三方代小程序开发,授权事件、消息与事件通知总结

大家好&#xff0c;我是小悟 关于抖音开放平台第三方代小程序开发的两个事件接收推送通知&#xff0c;是开放平台代小程序实现业务的重要功能。 授权事件推送和消息与事件推送类型都以Event的值判断。 授权事件推送通知 授权事件推送包括&#xff1a;推送票据、授权成功、授…

java 基础 IO字符流

1.汉字存储占多少字节&#xff1a; public class IoTest {public static void main(String[] args) {String str "abcd";String str1 "吴危险学java";System.out.println("字符串转为byte数组&#xff1a;" Arrays.toString(str.getBytes())…

C# RestoreFormer 图像修复

效果 项目 代码 using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; using System; using System.Collections.Generic; using System.Drawing; using System.Drawing.Imaging; using System.Windows.Forms;namespace 图像修复 {pu…

Python Opencv实践 - 车辆统计(1)读取视频,移除背景,做预处理

示例中的图像的腐蚀、膨胀和闭运算等需要根据具体视频进行实验得到最佳效果。代码仅供参考。 import cv2 as cv import numpy as np#读取视频文件 video cv.VideoCapture("../../SampleVideos/Traffic.mp4") FPS 10 DELAY int(1000 / FPS) kernel cv.getStructu…

【Jenkins使用】Jenkins 与 Git

一、概述 Jenkins 与 Git 的结合使用&#xff0c;可以理解为是 Jenkins 的一个强大之处。为什么要这么说&#xff0c;简要说明一下这个工作模式就能理解&#xff1a; 一个软件项目&#xff0c;开发过程中通常都会使用到一些源码管理工具&#xff0c;来达到团队协作的目的。而 …

C++ --STL

STL STL&#xff08;Standard Template Library,标准模板库&#xff09;STL从广义上分为&#xff1a; 容器&#xff08;container&#xff09;算法 (algorithm)迭代器 (iterator) 容器 和 算法之间通过迭代器进行无缝连接。STL几乎所有的代码都采用模板类或者模板函数 1、ST…

SystemC入门学习-第8章 测试平台的编写

之前的章节&#xff0c;一直把重点放在用SystemC来描述硬件电路上&#xff0c;即如何编写SystemC 的RTL。本章的注意力集中在验证和编写测试平台上。 重点包括&#xff1a; 如何生成时钟信号和激励波形如何编写有响应能力的测试平台如何记录仿真结果 8.1 编写测试平台 测试平…

【Shell】Shell脚本入门

Shell脚本入门 疑问 linux系统是如何操作计算机硬件CPU,内存,磁盘,显示器等? 答: 使用linux的内核操作计算机的硬件 Shell介绍 通过编写Shell命令发送给linux内核去执行, 操作就是计算机硬件. 所以Shell命令是用户操作计算机硬件的桥梁, Shell是命令, 类似于windows系统…