写点什么

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

作者:CTO技术共享
  • 2022 年 9 月 20 日
    广东
  • 本文字数:7790 字

    阅读完需:约 26 分钟

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

发布于: 刚刚阅读数: 4
用户头像

学如逆水行舟,不进则退 2022.08.05 加入

大型企业CTO,专注大数据、架构框架、集群、中间件、分布式、数据库、监控、开源、基础架构等技术分享,助力数字化转型。

评论

发布
暂无评论
springboot搭建基于minio的高性能存储_CTO技术共享_InfoQ写作社区