SpringBoot 搭建基于 minio 的高性能存储服务

Posted 芋道源码

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了SpringBoot 搭建基于 minio 的高性能存储服务相关的知识,希望对你有一定的参考价值。

点击上方“芋道源码”,选择“设为星标”

管她前浪,还是后浪?

能浪的浪,才是好浪!

每天 10:33 更新文章,每天掉亿点点头发...

源码精品专栏

 
  • 原创 | Java 2021 超神之路,很肝~

  • 中文详细注释的开源项目

  • RPC 框架 Dubbo 源码解析

  • 网络应用框架 Netty 源码解析

  • 消息中间件 RocketMQ 源码解析

  • 数据库中间件 Sharding-JDBC 和 MyCAT 源码解析

  • 作业调度中间件 Elastic-Job 源码解析

  • 分布式事务中间件 TCC-Transaction 源码解析

  • Eureka 和 Hystrix 源码解析

  • Java 并发源码

  • 来源:blog.csdn.net/weixin_45089791/

    article/details/116921075/

  • 什么是minio
  • 一. 使用docker 搭建minio 服务。
  • 二. 下面开始搭建springboot 环境

  • 什么是minio

    引用官网:

    MinIO是根据GNU Affero通用公共许可证v3.0发布的高性能对象存储。它与Amazon S3云存储服务兼容。使用MinIO构建用于机器学习,分析和应用程序数据工作负载的高性能基础架构。

    官网地址:

    https://min.io/

    文档地址:

    https://docs.min.io/

    基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能。

    项目地址:https://github.com/YunaiV/ruoyi-vue-pro

    一. 使用docker 搭建minio 服务。

    GNU / Linux和macOS

    docker run -p 9000:9000 \\
      --name minio1 \\
      -v /mnt/data:/data \\
      -e "MINIO_ROOT_USER=AKIAiosFODNN7EXAMPLE" \\
      -e "MINIO_ROOT_PASSWORD=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \\
      minio/minio server /data

    windows

    docker run -p 9000:9000 \\
      --name minio1 \\
      -v D:\\data:/data \\
      -e "MINIO_ROOT_USER=AKIAIOSFODNN7EXAMPLE" \\
      -e "MINIO_ROOT_PASSWORD=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY" \\
      minio/minio server /data
  • MINIO_ROOT_USER:为用户key
  • MINIO_ROOT_PASSWORD:为用户密钥
  • 以上搭建的都是单机版的。想要了解分布式 的方式请查看官网文档。

    这就是在win的docker上运行的。

    当启动后在浏览器访问http://localhost:9000就可以访问minio的图形化界面了,如图所示:

    基于微服务的思想,构建在 B2C 电商场景下的项目实战。核心技术栈,是 Spring Boot + Dubbo 。未来,会重构成 Spring Cloud Alibaba 。

    项目地址:https://github.com/YunaiV/onemall

    二. 下面开始搭建springboot 环境

    初始化一个springboot项目大家都会,这里不多做介绍。

    主要是介绍需要引入的依赖:

    <!-- thymeleaf模板渲染引擎-->
      <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-thymeleaf</artifactId>
            </dependency>
    <!-- 操作minio的java客户端-->
             <dependency>
                <groupId>io.minio</groupId>
                <artifactId>minio</artifactId>
                <version>8.2.1</version>
            </dependency>
    <!-- lombok插件-->
             <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>

    依赖可以官方文档里找:https://docs.min.io/docs/java-client-quickstart-guide.html

    下面介绍配置文件:

    spring:
      servlet:
        multipart:
          max-file-size: 10MB
          max-request-size: 10MB
    #minio配置
      minio:
        access-key: AKIAIOSFODNN7EXAMPLE      #key就是docker初始化是设置的,密钥相同
        secret-key: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
        url: http://localhost:9000
        bucket-name: wdhcr
      thymeleaf:
        cache: false

    创建minio的配置类:

    @Configuration
    @ConfigurationProperties(prefix = "spring.minio")
    @Data
    public class MinioConfiguration 
        private String accessKey;

        private String secretKey;

        private String url;

        private String bucketName;

        @Bean
        public MinioClient minioClient() 
            return MinioClient.builder()
                    .endpoint(url)
                    .credentials(accessKey, secretKey)
                    .build();
        

    使用配置属性绑定进行参数绑定,并初始化一个minio client对象放入容器中。

    下面就是我封装的minio client 操作minio的简单方法的组件。

    @Component
    public class MinioComp 

        @Autowired
        private MinioClient minioClient;

        @Autowired
        private MinioConfiguration configuration;

        /**
         * @description: 获取上传临时签名
         * @dateTime: 2021/5/13 14:12
         */

        public Map getPolicy(String fileName, ZonedDateTime time) 
            PostPolicy postPolicy = new PostPolicy(configuration.getBucketName(), time);
            postPolicy.addEqualsCondition("key", fileName);
            try 
                Map<String, String> map = minioClient.getPresignedPostFormData(postPolicy);
                HashMap<String, String> map1 = new HashMap<>();
                map.forEach((k,v)->
                   map1.put(k.replaceAll("-",""),v);
               );
                map1.put("host",configuration.getUrl()+"/"+configuration.getBucketName());
                return map1;
             catch (ErrorResponseException e) 
                e.printStackTrace();
             catch (InsufficientDataException e) 
                e.printStackTrace();
             catch (InternalException e) 
                e.printStackTrace();
             catch (InvalidKeyException e) 
                e.printStackTrace();
             catch (InvalidResponseException e) 
                e.printStackTrace();
             catch (IOException e) 
                e.printStackTrace();
             catch (NoSuchAlgorithmException e) 
                e.printStackTrace();
             catch (ServerException e) 
                e.printStackTrace();
             catch (XmlParserException e) 
                e.printStackTrace();
            
            return null;
        

        /**
         * @description: 获取上传文件的url
         * @dateTime: 2021/5/13 14:15
         */

        public String getPolicyUrl(String objectName, Method method, int time, TimeUnit timeUnit) 
            try 
                return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                        .method(method)
                        .bucket(configuration.getBucketName())
                        .object(objectName)
                        .expiry(time, timeUnit).build());
             catch (ErrorResponseException e) 
                e.printStackTrace();
             catch (InsufficientDataException e) 
                e.printStackTrace();
             catch (InternalException e) 
                e.printStackTrace();
             catch (InvalidKeyException e) 
                e.printStackTrace();
             catch (InvalidResponseException e) 
                e.printStackTrace();
             catch (IOException e) 
                e.printStackTrace();
             catch (NoSuchAlgorithmException e) 
                e.printStackTrace();
             catch (XmlParserException e) 
                e.printStackTrace();
             catch (ServerException e) 
                e.printStackTrace();
            
            return null;
        


        /**
         * @description: 上传文件
         * @dateTime: 2021/5/13 14:17
         */

        public void upload(MultipartFile file, String fileName) 
            // 使用putObject上传一个文件到存储桶中。
            try 
                InputStream inputStream = file.getInputStream();
                minioClient.putObject(PutObjectArgs.builder()
                        .bucket(configuration.getBucketName())
                        .object(fileName)
                        .stream(inputStream, file.getSize(), -1)
                        .contentType(file.getContentType())
                        .build());
             catch (ErrorResponseException e) 
                e.printStackTrace();
             catch (InsufficientDataException e) 
                e.printStackTrace();
             catch (InternalException e) 
                e.printStackTrace();
             catch (InvalidKeyException e) 
                e.printStackTrace();
             catch (InvalidResponseException e) 
                e.printStackTrace();
             catch (IOException e) 
                e.printStackTrace();
             catch (NoSuchAlgorithmException e) 
                e.printStackTrace();
             catch (ServerException e) 
                e.printStackTrace();
             catch (XmlParserException e) 
                e.printStackTrace();
            
        
      /**
         * @description: 根据filename获取文件访问地址
         * @dateTime: 2021/5/17 11:28
         */

        public String getUrl(String objectName, int time, TimeUnit timeUnit) 
            String url = null;
            try 
                url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(configuration.getBucketName())
                        .object(objectName)
                        .expiry(time, timeUnit).build());
             catch (ErrorResponseException e) 
                e.printStackTrace();
             catch (InsufficientDataException e) 
                e.printStackTrace();
             catch (InternalException e) 
                e.printStackTrace();
             catch (InvalidKeyException e) 
                e.printStackTrace();
             catch (InvalidResponseException e) 
                e.printStackTrace();
             catch (IOException e) 
                e.printStackTrace();
             catch (NoSuchAlgorithmException e) 
                e.printStackTrace();
             catch (XmlParserException e) 
                e.printStackTrace();
             catch (ServerException e) 
                e.printStackTrace();
            
            return url;
        

    简单说明:

  • 使用MultipartFile接收前端文件流,再上传到minio。
  • 构建一个formData的签名数据,给前端,让前端之前上传到minio。
  • 构建一个可以上传的临时URL给前端,前端通过携带文件请求该URL进行上传。
  • 使用filename请求服务端获取临时访问文件的URL。(最长时间为7 天,想要永久性访问,需要其他设置,这里不做说明。)
  • 下面展示页面html,使用的是VUE+element-ui进行渲染。

    <!DOCTYPE html>
    <html>
    <head>
        <meta charset="UTF-8">
        <!-- import CSS -->
        <link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">
        <title>上传图片</title>
    </head>
    <body>
    <div id="app">

        <el-row :gutter="2">
            <el-col :span="8">
                <div class="div-center-class">
                    <div class="">
                        <center><h3>传统上传</h3></center>
                        <el-upload
                                class="upload-demo"
                                action="#"
                                drag
                                :http-request="uploadHandle">

                            <i class="el-icon-upload"></i>
                            <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
                            <div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过500kb</div>
                        </el-upload>
                        <div v-if="imgUrl">
                            <img :src="imgUrl" style="width: 40px;height: 40px"></img>
                        </div>
                    </div>
                </div>
            </el-col>
            <el-col :span="8">
                <div class="div-center-class">
                    <div class="">
                        <center><h3>前端formData直传</h3></center>
                        <el-upload
                                class="upload-demo"
                                action="#"
                                drag
                                :http-request="httpRequestHandle">

                            <i class="el-icon-upload"></i>
                            <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
                            <div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过500kb</div>
                        </el-upload>
                        <div v-if="directUrl">
                            <img :src="directUrl" style="width: 40px;height: 40px"></img>
                        </div>
                    </div>
                </div>
            </el-col>
            <el-col :span="8">
                <div class="div-center-class">
                    <div class="">
                        <center><h3>前端Url直传</h3></center>
                        <el-upload
                                class="upload-demo"
                                action="#"
                                drag
                                :http-request="UrlUploadHandle">

                            <i class="el-icon-upload"></i>
                            <div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
                            <div class="el-upload__tip" slot="tip">只能上传jpg/png文件,且不超过500kb</div>
                        </el-upload>
                        <div v-if="uploadUrl">
                            <img :src="uploadUrl" style="width: 40px;height: 40px"></img>
                        </div>
                    </div>
                </div>
            </el-col>
        </el-row>
    </div>
    </body>
    <!-- import Vue before Element -->
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <!-- import javascript -->
    <script src="https://unpkg.com/element-ui/lib/index.js"></script>
    <!--import  axios -->
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>
    <script>
        new Vue(
            el\'#app\',
            datafunction (
                return 
                    imgUrl\'\',
                    directUrl\'\',
                    uploadUrl\'\'
                
            ,
            methods

                uploadHandle(options) 
                    let file = options;
                    this.traditionPost(file);
                ,
                traditionPost(file) 
                    _that = this
                    const form = new FormData();
                    form.append("fileName", file.name);
                    form.append("file", file);
                    this.axiosPost("post""/upload", form).then(function (res
                        if (res.status === 200
                            _that.imgUrl = res.data.data
                         else 
                            alert("上传失败!")
                        
                    )
                ,
                getpolicy(file) 
                    _that = this
                    axios.get(\'policy?fileName=\' + file.name)
                        .then(function (response
                            let xamzalgorithm, xamzcredential, policy, xamzsignature, xamzdate, host = response.data.data;
                            let formData = new FormData();
                            formData.append("key", file.name);
                            formData.append("x-amz-algorithm", xamzalgorithm);  // 让服务端返回200,不设置则默认返回204。
                            formData.append("x-amz-credential", xamzcredential);
                            formData.append("policy", policy);
                            formData.append("x-amz-signature", xamzsignature);
                            formData.append("x-amz-date", xamzdate);
                            formData.append("file", file);
                            // 发送 POST 请求
                            _that.axiosPost("post", host, formData).then(function (res
                                if (res.status === 204
                                    axios.get(\'url?fileName=\' + file.name).then(function (res
                                        _that.directUrl = res.data.data;
                                    )
                                 else 
                                    alert("上传失败!")
                                
                            )
                        )
                ,
                httpRequestHandle(options) 
                    let file = options;
                    this.getpolicy(file);
                ,

                UrlUploadHandle(options) 
                    let file = options;
                    this.getUploadUrl(file);
                ,
                getUploadUrl(file) 
                    _that = this
                    console.log(file)
                    axios.get(\'uploadUrl?fileName=\' + file.name)
                        .then(function (response
                            let url = response.data.data;
                            // 发送 put 请求
                            let config = \'Content-Type\': file.type
                            _that.axiosPost("put", url, file, config).then(function (res
                                if (res.status === 200
                                    axios.get(\'url?fileName=\' + file.name).then(function (res
                                        _that.uploadUrl = res.data.data;
                                    )
                                 else 
                                    alert("上传失败!")
                                
                            )
                        )
                ,
                //封装
                //axios封装post请求
                axiosPost(method, url, data, config) 
                    let result = axios(
                        method: method,
                        url: url,
                        data: data,
                        headers: config
                    ).then(resp => 
                        return resp
                    ).catch(error => 
                        return "exception=" + error;
                    );
                    return result;
                

            
        )
    </script>
    <style>
        .div-center-class 
            padding28% 0%;
            text-align: center;
            background: beige;
        
    </style>
    </html>

    页面效果

    可以分别体验不同的实现效果。

    以上就是使用springboot搭建基于minio的高性能存储服务的全部步骤了。

    项目地址是:

    https://gitee.com/jack_whh/minio-upload



    欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

    已在知识星球更新源码解析如下:

    最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

    提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。

    获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

    文章有帮助的话,在看,转发吧。

    谢谢支持哟 (*^__^*)

    minio笔记3--基于k8s搭建minio集群


    minio笔记3--基于k8s搭建minio集群

    介绍

    笔者在 ​​minio笔记01–部署与测试minio​​​ 和 ​​minio笔记02–基于swarm搭建minio集群​​​ 中介绍了minio 的集群搭建步骤和基础使用方法,本文继上述两篇文章介绍k8s下的minio搭建过程。其实很早就准备写一份基于k8s的集群搭建方式,但是由于各种原因耽搁很久了,最近再次发了些时间完成了k8s下的minio部署,将步骤分享在此处以供有需要的小伙伴们学习!
    后续也会在此处继续更新 k8s下minio的部署和常见注意事项!

    安装单个节点

    1. 部署服务
    1. 添加 helm 源
    helm repo add minio https://helm.min.io/
    2. 下载chart压缩包
    helm fetch minio/minio
    3. 解压并按需更改values.yaml
    tar zxvf minio-8.0.10.tgz
    vim values.yaml
    resources:
    requests:
    memory: 256Mi
    4. 部署实例
    helm install minio \\
    --namespace minio \\
    --create-namespace \\
    --set accessKey=minio,secretKey=minio123 \\
    --set mode=standalone \\
    --set service.type=NodePort \\
    --set persistence.enabled=false \\
    --set persistence.size=10Gi \\
    1. 测试
      集群deployment 和 svc
    2. minio笔记3--基于k8s搭建minio集群_基于k8s搭建minio集群

    3. web 界面:
    4. minio笔记3--基于k8s搭建minio集群_minio_02


    5. minio笔记3--基于k8s搭建minio集群_对象存储_03

    安装 minio 集群

    1. 部署服务
    1. 添加 helm 源
    helm repo add minio https://helm.min.io/
    2. 下载chart压缩包
    helm fetch minio/minio
    3. 解压并按需更改values.yaml
    tar zxvf minio-8.0.10.tgz
    vim values.yaml
    resources:
    requests:
    memory: 256Mi
    4. 部署实例
    helm install minio \\
    --namespace minio --create-namespace \\
    --set accessKey=minio,secretKey=minio123 \\
    --set mode=distributed \\
    --set replicas=4 \\
    --set service.type=NodePort \\
    --set persistence.enabled=false \\
    --set persistence.size=10Gi \\
    1. 测试
      集群statefulset、svc 和 endpoints
      kubectl -n minio get statefulset,svc,endpoints minio
    2. minio笔记3--基于k8s搭建minio集群_minio_04

    3. web 界面:
      新建bucket test01,test02,并各自上传一个文件
    4. minio笔记3--基于k8s搭建minio集群_k8s_05

    5. mc 命令查看集群信息
      mc alias set local http://192.168.2.131:32000 minio minio123
      mc ls local 查看文件信息
    6. minio笔记3--基于k8s搭建minio集群_k8s_06

    7. mc admin info local 查看集群信息
    8. minio笔记3--基于k8s搭建minio集群_持久化存储_07

    9. 由此可见,已经部署了一个4节点的minio集群。

    注意事项

    1. 本文主要是用于测试,由此使用容器的临时存储,如果在生产上使用者需要指定动态存储类,可以使用 nfs、longhorn、ceph 等持久化存储。
      更多持久化存储见:​​​k8s storage-classes​​ 笔者新建nfs-client 存储类后,通过nfs动态分配存储空间,从而实现了minio 的持久化存储。
    2. helm install minio \\
      --namespace minio --create-namespace \\
      --set accessKey=minio,secretKey=minio123 \\
      --set mode=distributed \\
      --set replicas=4 \\
      --set service.type=NodePort \\
      --set persistence.enabled=true \\
      --set persistence.size=500Mi \\
      --set persistence.storageClass=nfs-client \\
      minio-8.0.10/minio

      存储路径如下:
      /data/nfs$ ls

    说明

    1. 软件环境
      k8s 版本: 1.19.4
      helm chart 版本: minio/minio:minio-8.0.10
      minio镜像版本:minio/minio:RELEASE.2021-02-14T04-01-33Z
    2. 参考文档
      ​​​1 minio 高性能 Kubernetes 原生对象存储​​​​2 kubernetes部署minio对象存储​​​​3 minio 官方文档​​​​4 k8s中创建nfs存储类(公共盘)​


    以上是关于SpringBoot 搭建基于 minio 的高性能存储服务的主要内容,如果未能解决你的问题,请参考以下文章

    MinIO 搭建使用

    高可用对象存储实战使用Python操作Minio存储桶

    minio笔记3--基于k8s搭建minio集群

    SpringBoot 使用 Minio 进行文件存储

    MinIO 集群搭建

    SpringBoot+Minio搭建不再爆肝秃头的分布式文件服务器