springboot 搭建基于 minio 的高性能存储

简介: springboot 搭建基于 minio 的高性能存储


网络异常,图片无法展示
|

什么是 minio

引用官网:

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

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

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


一. 使用 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 的图形化界面了,如图所示:


网络异常,图片无法展示
|


网络异常,图片无法展示
|


二. 下面开始搭建 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")@Datapublic 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 的简单方法的组件。


@Componentpublic 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',        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 的高性能存储服务的全部步骤了。


相关文章
|
24天前
|
XML Java API
SpringBoot 整合 Minio
本文介绍了如何在服务器上安装并配置Minio服务,包括Minio的依赖、配置类以及基本操作。首先,通过Maven添加Minio依赖;接着,在`yml`文件中配置Minio的连接信息;然后,创建`MinIoClientConfig`类将MinioClient注入到Spring容器中;最后,定义`OSSFileService`接口及其实现类`OssFileServiceImpl`,实现文件上传、获取文件URL、临时访问URL和删除文件等操作。
|
3月前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
|
24天前
|
XML Java Kotlin
springboot + minio + kkfile实现文件预览
本文介绍了如何在容器中安装和启动kkfileviewer,并通过Spring Boot集成MinIO实现文件上传与预览功能。首先,通过下载kkfileviewer源码并构建Docker镜像来部署文件预览服务。接着,在Spring Boot项目中添加MinIO依赖,配置MinIO客户端,并实现文件上传与获取预览链接的接口。最后,通过测试验证文件上传和预览功能的正确性。
springboot + minio + kkfile实现文件预览
|
13天前
|
存储 前端开发 JavaScript
|
13天前
|
存储 Java API
|
1月前
|
存储 前端开发 Java
springboot整合最新版minio和minio的安装(完整教程,新人必看)
本文详细介绍了如何使用Docker安装配置最新版的MinIO,并展示了如何在Spring Boot应用中整合MinIO以及如何通过前端进行文件上传测试。
169 3
springboot整合最新版minio和minio的安装(完整教程,新人必看)
|
1月前
|
存储 前端开发 Java
Spring Boot 集成 MinIO 与 KKFile 实现文件预览功能
本文详细介绍如何在Spring Boot项目中集成MinIO对象存储系统与KKFileView文件预览工具,实现文件上传及在线预览功能。首先搭建MinIO服务器,并在Spring Boot中配置MinIO SDK进行文件管理;接着通过KKFileView提供文件预览服务,最终实现文档管理系统的高效文件处理能力。
262 11
|
2月前
|
缓存 监控 Java
造轮子能力大提升:基于SpringBoot打造高性能缓存组件
在快节奏的软件开发领域,"不重复造轮子" 常常被视为提高效率的金科玉律。然而,在某些特定场景下,定制化的高性能缓存组件却是提升系统性能、优化用户体验的关键。今天,我们将深入探讨如何利用SpringBoot框架,从零开始打造一款符合项目需求的高性能缓存组件,分享我在这一过程中的技术心得与学习体会。
69 6
|
3月前
|
监控 Java API
Spring Boot中的异步革命:构建高性能的现代Web应用
【8月更文挑战第29天】Spring Boot 是一个简化 Spring 应用开发与部署的框架。异步任务处理通过后台线程执行耗时操作,提升用户体验和系统并发能力。要在 Spring Boot 中启用异步任务,需在配置类上添加 `@EnableAsync` 注解,并定义一个自定义的 `ThreadPoolTaskExecutor` 或使用默认线程池。通过 `@Async` 注解的方法将在异步线程中执行。异步任务适用于发送电子邮件、数据处理、外部 API 调用和定时任务等场景。最佳实践中应注意正确配置线程池、处理返回值和异常、以及监控任务状态,确保系统的稳定性和健壮性。
41 0
|
3月前
|
缓存 Java Spring
Java本地高性能缓存实践问题之在Spring Boot中启用缓存支持的问题如何解决
Java本地高性能缓存实践问题之在Spring Boot中启用缓存支持的问题如何解决