Spring Boot 整合 Minio 实现文件上传

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot 整合 Minio 实现文件上传相关的知识,希望对你有一定的参考价值。

参考技术A 文章目录:

MinIO 是一个用 Golang 开发的基于 Apache License v2.0 源协议的对象存储服务。

它兼容亚马逊 S3 云存储服务接口,适合存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,单个文件可以是任意大小,从几kb到最大5T不等。

使用 docker 安装

说明:

9020 为控制台访问端口。

创建存储桶:

设置存储桶权限:

MinioProperties.java

MinioConfig.java

FILE_NAME_PATTERN 是上传文件的命名格式。第一个 放的是上传时间,格式是 yyyyMMddHHmmss ,精确到秒。第二个 放的是上传文件的原始文件名。

Spring Boot + minio 实现高性能存储服务,So Easy~!

点击关注公众号,Java干货及时送达

什么是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项目大家都会,这里不多做介绍。

推荐一个 Spring Boot 基础教程及实战示例:https://github.com/javastacks/spring-boot-best-practice

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

<!-- 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>

另外,这份Spring Boot 学习笔记分享给你。

依赖可以官方文档里找: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对象放入容器中。最新 Spring Boot 面试题整理好了,大家可以在Java面试库小程序在线刷题。

下面就是我封装的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;
    

简单说明:

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

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

原文链接:https://blog.csdn.net/weixin_45089791/article/details/116921075

版权声明:本文为CSDN博主「HUWD」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

End

Spring Boot 学习笔记,这个太全了!

23 种设计模式实战(很全)

Nacos 2.1.1 正式发布,真心强!

国内最强微服务框架,没有之一!

面试通过,背调凉了。。

Spring Cloud 微服务最新教程!

以上是关于Spring Boot 整合 Minio 实现文件上传的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot + minio 实现高性能存储服务,So Easy~!

微服务Spring Boot 整合 Redis 实现 好友关注

minio单机集群搭建Spring Boot集成,详细步骤

Spring boot整合jsp

Minio 整合springboot 开发 实现文件上传

Spring boot 入门三:spring boot 整合mybatis 实现CRUD操作