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

1.什么是MinIO

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

官网地址:https://min.io/

文档地址:https://docs.min.io/

2.使用docker 搭建minio 服务

2.1.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

2.2.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:为用户密钥

以上搭建的都是单机版的。想要了解分布式 的方式请查看官网文档。

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

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

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

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

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

3.下面开始搭建springboot 环境

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

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

<dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-thymeleafartifactId>
dependency>

<dependency>
    <groupId>io.miniogroupId>
    <artifactId>minioartifactId>
    <version>8.2.1version>
dependency>

<dependency>
    <groupId>org.projectlombokgroupId>
    <artifactId>lombokartifactId>
    <optional>trueoptional>
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 map = minioClient.getPresignedPostFormData(postPolicy);
            HashMap 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">

    <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文件,且不超过500kbdiv>
                    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文件,且不超过500kbdiv>
                    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文件,且不超过500kbdiv>
                    el-upload>
                    <div v-if="uploadUrl">
                        <img :src="uploadUrl" style="width: 40px;height: 40px">img>
                    div>
                div>
            div>
        el-col>
    el-row>
div>
body>

<script src="https://unpkg.com/vue/dist/vue.js">script>

<script src="https://unpkg.com/element-ui/lib/index.js">script>

<script src="https://unpkg.com/axios/dist/axios.min.js">script>
<script>
    new Vue({
        el: '#app',
        data: function () {
            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 {
        padding: 28% 0%;
        text-align: center;
        background: beige;
    }
style>
html>

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

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

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

项目地址是: https://gitee.com/jack_whh/minio-upload

Original: https://www.cnblogs.com/JimmyThomas/p/15991618.html
Author: JimmyThomas
Title: SpringBoot 搭建基于 MinIO 的高性能存储服务

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/582522/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • 三少玩Linux之FreeBSD安装与简单配置,与win7共存安装方法

    还是先安装win7, 这里就不说了; 这个是FreeBSD安装视频: https://www.bilibili.com/video/BV1vk4y1d7jt。 记得先安装windo…

    Linux 2023年6月14日
    0116
  • 【PHP】浅谈php实现订阅发布模式及redis的实现

    订阅发布应用场景: 1、广告推送 2、游戏公告 3、广播–短信、邮件 4、跨应用推送–使用同一个redis实例,net发布publish,php常驻内存订阅subscribe处…

    Linux 2023年5月28日
    086
  • 【XML】学习笔记第三章-namesapce

    命名空间概述 标记中出现了同名不同义的情况,极其容易造成含义混乱。命名空间就是由W3C制定的用于解决这类问题的。 【命名空间的作用】出现标记同名不同义情况时,避免含义混乱 XML技…

    Linux 2023年6月14日
    089
  • 同城双活概述

    引言 同城双活,是年度最大的架构变更。同城容灾,对于生产的高可用保障,重大的意义和价值是不言而喻的。 用储总的话说,这么重要的架构工作,所有架构师都应该重点主导和参与。 同城双活,…

    Linux 2023年6月14日
    0129
  • Linux常用文件权限命令详解

    pwd pwd命令用于获取当前工作目录的绝对路径。 使用示例: pwd 效果如下图: cd cd命令用于切换工作目录。 使用示例: cd &#x4E07;&#x73…

    Linux 2023年5月27日
    0119
  • 异步、邮件、定时任务

    异步、邮件、定时任务 14.1 异步任务 编写一个业务测试类 文件路径:com–dzj–service–AsynService.java @Se…

    Linux 2023年6月14日
    0110
  • Linux目录结构信息

    一、系统目录结构介绍 1.1 常见系统目录结构 Windows下: C:\windows D:\Program Files Linux下: /etc/sysconfig /usr/…

    Linux 2023年5月27日
    083
  • 数据结构-表

    顺序表 #ifndef SEQLIST_H #define SEQLIST_H typedef int DataType; struct Node { int MaxNum; in…

    Linux 2023年6月7日
    081
  • HTS-一键启动

    #!/bin/bash #program:runall and checkall process #author:sundz 20220606 v1 如果此用户不存在这个,则配置为…

    Linux 2023年6月7日
    0112
  • Docker部署Redis

    执行以下命令,创建并启动镜像。如果本地没镜像,会自动拉取 docker run \ -itd \ –restart=always \ –name=redis \ -p 6379…

    Linux 2023年6月7日
    090
  • burp通过条件竞争上传文件

    一·何为条件竞争现代框架更能抵御此类攻击。他们通常不会将文件直接上传到文件系统上的预期目的地。相反,他们采取了预防措施,例如首先上传到临时的沙盒目录并随机命名以避免覆盖现有文件。然…

    Linux 2023年6月7日
    093
  • 网络安全简单入门与扫描

    网络安全简单入门 内容大纲 策略制定 安全工具 其他 1、安全策略 1.1、安全三要素 要全面地认识一个安全问题,我们有很多种办法,但首先要理解安全问题的组成属性。前人通过无数实践…

    Linux 2023年6月7日
    0103
  • PowerShell yarn : 无法加载文件 C:UsersAdminAppDataRoamingnpmyarn.ps1,因为在此系统因为在此系统上禁止运行脚本。

    1、搜索powershell,右键以管理员身份运行 2、若要在本地计算机上运行您编写的未签名脚本和来自其他用户的签名脚本,请使用以下命令将计算机上的 执行策略更改为 RemoteS…

    Linux 2023年5月28日
    0116
  • 综合布线 子网掩码 IP地址 子网划分

    A类地址:1.0.0.0~126.255.255.255 255.0.0.0 B类地址:128.0.0.0~191.255.255.255 255.255.0.0 C类地址:192…

    Linux 2023年6月6日
    097
  • 【Example】C++ std::thread 及 std::mutex

    与 Unix 下的 thread 不同的是,C++ 标准库当中的 std::thread 功能更加简单,可以支持跨平台特性。 因此在项目需要跨平台及对多线程简单应用情况下,应优先考…

    Linux 2023年6月13日
    069
  • MySQL — 数据控制语言

    DCL 全称 Data Control Language。数据控制语言,用来创建数据库用户、控制数据库的访问权限。 1、用户管理 select * from user; 只能在指定…

    Linux 2023年6月8日
    0105
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球