• SpringBoot 整合 Minio 上传文件


    SpringBoot 整合 Minio 上传文件
    Minio
    上传文件如果不使用云服务的话,需要本地搭建,一般选择 FastDFS 但是 FastDFS 安装比较复杂,今天了解一款安装使用更简单的存储系统 MinIO

    MinIO 是一款高性能、分布式的对象存储系统. 它是一款软件产品, 可以100%的运行在标准硬件。即X86等低成本机器也能够很好的运行MinIO。

    MinIO与传统的存储和其他的对象存储不同的是:它一开始就针对性能要求更高的私有云标准进行软件架构设计。因为MinIO一开始就只为对象存储而设计。所以他采用了更易用的方式进行设计,它能实现对象存储所需要的全部功能,在性能上也更加强劲,它不会为了更多的业务功能而妥协,失去MinIO的易用性、高效性。 这样的结果所带来的好处是:它能够更简单的实现局有弹性伸缩能力的原生对象存储服务。

    MinIO在传统对象存储用例(例如辅助存储,灾难恢复和归档)方面表现出色。同时,它在机器学习、大数据、私有云、混合云等方面的存储技术上也独树一帜。当然,也不排除数据分析、高性能应用负载、原生云的支持。

    在中国:阿里巴巴、腾讯、百度、中国联通、华为、中国移动等等9000多家企业也都在使用MinIO产品

    安装 Minio
    使用docker安装
    拉取镜像
    docker pull minio/minio
    docker pull minio/minio
    启动
    docker run -p 9000:9000 -p 9001:9001 -d --name minio -v /opt/docker/minio/data:/data -v /opt/docker/minio/config:/root/.minio -e “MINIO_ROOT_USER=minio” -e “MINIO_ROOT_PASSWORD=minio@123456” minio/minio server /data --console-address “:9000” --address “:9001”
    docker run -p 9000:9000 -p 9001:9001 -d --name minio -v /opt/docker/minio/data:/data -v /opt/docker/minio/config:/root/.minio -e “MINIO_ROOT_USER=minio” -e “MINIO_ROOT_PASSWORD=minio@123456” minio/minio server /data --console-address “:9000” --address “:9001”
    使用9000端口 登录控制台

    创建存储桶

    设置桶权限

    创建 Java 客户端
    依赖

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
    
        <!-- minio依赖       -->
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>8.2.1</version>
        </dependency>
    
        <!-- hutool工具类       -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.1.2</version>
        </dependency>
    
        <!-- 压缩图片       -->
        <dependency>
            <groupId>net.coobird</groupId>
            <artifactId>thumbnailator</artifactId>
            <version>0.4.8</version>
        </dependency>
    </dependencies>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    配置文件

    spring:
      application:
        name: minio-demo
      servlet:
        multipart:
          max-file-size: 20MB
          max-request-size: 200MB
     
    server:
      port: 8088
     
    minio:
      endpoint: http://你的ip:9001
      accessKey: minio
      secretKey: minio@123456
      nginxHost: http://你的域名
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    配置文件配置类

    package com.sqm.minio_demo.config;
     
    import lombok.Data;
    import org.springframework.boot.context.properties.ConfigurationProperties;
    import org.springframework.stereotype.Component;
     
    @ConfigurationProperties(prefix = "minio")
    @Component
    @Data
    public class MinioProperties {
     
        /**
         * 连接地址
         */
        private String endpoint;
        /**
         * 用户名
         */
        private String accessKey;
        /**
         * 密码
         */
        private String secretKey;
        /**
         * 域名
         */
        private String nginxHost;
     
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    package com.sqm.minio_demo.config;
    
    import io.minio.MinioClient;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.context.properties.EnableConfigurationProperties;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @EnableConfigurationProperties(MinioProperties.class)
    public class MinioConfig {
    
        @Autowired
        private MinioProperties minioProperties;
    
        @Bean
        public MinioClient minioClient(){
            return MinioClient.builder()
                    .endpoint(minioProperties.getEndpoint())
                    .credentials(minioProperties.getAccessKey(),minioProperties.getSecretKey())
                    .build();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    文件地址返回路径实体类

    package com.sqm.minio_demo.entity;
     
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
     
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class UploadResponse {
     
        private String minIoUrl;
     
        private String nginxUrl;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    上传文件工具类

    package com.sqm.minio_demo.util;
     
    import cn.hutool.core.date.DateUtil;
    import com.sqm.minio_demo.config.MinioProperties;
    import com.sqm.minio_demo.entity.UploadResponse;
    import io.minio.*;
    import io.minio.errors.*;
    import io.minio.messages.Bucket;
    import lombok.extern.slf4j.Slf4j;
    import net.coobird.thumbnailator.Thumbnails;
     
    import org.apache.commons.fileupload.FileItem;
     
    import org.apache.commons.fileupload.FileItemFactory;
    import org.apache.commons.fileupload.disk.DiskFileItemFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    import org.springframework.web.multipart.MultipartFile;
    import org.springframework.web.multipart.commons.CommonsMultipartFile;
     
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.security.InvalidKeyException;
    import java.security.NoSuchAlgorithmException;
    import java.util.List;
    import java.util.Optional;
    import java.util.Random;
     
    @Component
    @Slf4j
    public class MinioUtil {
     
        @Autowired
        private MinioProperties minioProperties;
     
        @Autowired
        private MinioClient minioClient;
     
        private final Long maxSize = (long) (1024 * 1024);
     
        /**
         * 创建bucket
         */
        public void createBucket(String bucketName) throws Exception {
            if (!minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build())) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
            }
        }
     
        /**
         * 上传文件
         */
        public UploadResponse uploadFile(MultipartFile file, String bucketName) throws Exception {
            //判断文件是否为空
            if (null == file || 0 == file.getSize()) {
                return null;
            }
            //判断存储桶是否存在  不存在则创建
            createBucket(bucketName);
            //文件名
            String originalFilename = file.getOriginalFilename();
            //新的文件名 = 时间戳_随机数.后缀名
            assert originalFilename != null;
            long now = System.currentTimeMillis() / 1000;
            String fileName = DateUtil.format(DateUtil.date(),"yyyyMMdd")+"_"+ now + "_" + new Random().nextInt(1000) +
                    originalFilename.substring(originalFilename.lastIndexOf("."));
            //开始上传
            log.info("file压缩前大小:{}",file.getSize());
            if (file.getSize() > maxSize) {
                FileItemFactory fileItemFactory = new DiskFileItemFactory();
                FileItem fileItem = fileItemFactory.createItem(fileName, "text/plain", true, fileName);
                OutputStream outputStream = fileItem.getOutputStream();
                Thumbnails.of(file.getInputStream()).scale(1f).outputFormat(originalFilename.substring(originalFilename.lastIndexOf(".")+1)).outputQuality(0.25f).toOutputStream(outputStream);
                file = new CommonsMultipartFile(fileItem);
            }
            log.info("file压缩后大小:{}",file.getSize());
            minioClient.putObject(
                    PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(
                            file.getInputStream(), file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build());
            String url = minioProperties.getEndpoint() + "/" + bucketName + "/" + fileName;
            String urlHost = minioProperties.getNginxHost() + "/" + bucketName + "/" + fileName;
            return new UploadResponse(url, urlHost);
        }
     
        /**
         * 获取全部bucket
         *
         * @return
         */
        public List<Bucket> getAllBuckets() throws Exception {
            return minioClient.listBuckets();
        }
     
        /**
         * 根据bucketName获取信息
         *
         * @param bucketName bucket名称
         */
        public Optional<Bucket> getBucket(String bucketName) throws IOException, InvalidKeyException, NoSuchAlgorithmException, InsufficientDataException, InvalidResponseException, InternalException, ErrorResponseException, ServerException, XmlParserException, ServerException {
            return minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
        }
     
        /**
         * 根据bucketName删除信息
         *
         * @param bucketName bucket名称
         */
        public void removeBucket(String bucketName) throws Exception {
            minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
        }
     
        /**
         * 获取⽂件外链
         *
         * @param bucketName bucket名称
         * @param objectName ⽂件名称
         * @param expires    过期时间 <=7
         * @return url
         */
        public String getObjectURL(String bucketName, String objectName, Integer expires) throws Exception {
            return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(objectName).expiry(expires).build());
        }
     
        /**
         * 获取⽂件
         *
         * @param bucketName bucket名称
         * @param objectName ⽂件名称
         * @return ⼆进制流
         */
        public InputStream getObject(String bucketName, String objectName) throws Exception {
            return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
        }
     
        /**
         * 上传⽂件
         *
         * @param bucketName bucket名称
         * @param objectName ⽂件名称
         * @param stream     ⽂件流
         * @throws Exception https://docs.minio.io/cn/java-minioClient-api-reference.html#putObject
         */
        public void putObject(String bucketName, String objectName, InputStream stream) throws
                Exception {
            minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, stream.available(), -1).contentType(objectName.substring(objectName.lastIndexOf("."))).build());
        }
     
        /**
         * 上传⽂件
         *
         * @param bucketName  bucket名称
         * @param objectName  ⽂件名称
         * @param stream      ⽂件流
         * @param size        ⼤⼩
         * @param contextType 类型
         * @throws Exception https://docs.minio.io/cn/java-minioClient-api-reference.html#putObject
         */
        public void putObject(String bucketName, String objectName, InputStream stream, long
                size, String contextType) throws Exception {
            minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, size, -1).contentType(contextType).build());
        }
     
        /**
         * 获取⽂件信息
         *
         * @param bucketName bucket名称
         * @param objectName ⽂件名称
         * @throws Exception https://docs.minio.io/cn/java-minioClient-api-reference.html#statObject
         */
        public StatObjectResponse getObjectInfo(String bucketName, String objectName) throws Exception {
            return minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
        }
     
        /**
         * 删除⽂件
         *
         * @param bucketName bucket名称
         * @param objectName ⽂件名称
         * @throws Exception https://docs.minio.io/cn/java-minioClient-apireference.html#removeObject
         */
        public void removeObject(String bucketName, String objectName) throws Exception {
            minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
        }
     
        /***
         * 上传视频
         * @param file
         * @param bucketName
         * @return
         * @throws Exception
         */
        public UploadResponse uploadVideo(MultipartFile file, String bucketName) throws Exception {
            //判断文件是否为空
            if (null == file || 0 == file.getSize()) {
                return null;
            }
            //判断存储桶是否存在  不存在则创建
            createBucket(bucketName);
            //文件名
            String originalFilename = file.getOriginalFilename();
            //新的文件名 = 时间戳_随机数.后缀名
            assert originalFilename != null;
            long now = System.currentTimeMillis() / 1000;
            String fileName = DateUtil.format(DateUtil.date(),"yyyyMMdd")+"_"+ now + "_" + new Random().nextInt(1000) +
                    originalFilename.substring(originalFilename.lastIndexOf("."));
            //开始上传
            log.info("file大小:{}",file.getSize());
            minioClient.putObject(
                    PutObjectArgs.builder().bucket(bucketName).object(fileName).stream(
                            file.getInputStream(), file.getSize(), -1)
                            .contentType("video/mp4")
                            .build());
            String url = minioProperties.getEndpoint() + "/" + bucketName + "/" + fileName;
            String urlHost = minioProperties.getNginxHost() + "/" + bucketName + "/" + fileName;
            return new UploadResponse(url, urlHost);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
    • 140
    • 141
    • 142
    • 143
    • 144
    • 145
    • 146
    • 147
    • 148
    • 149
    • 150
    • 151
    • 152
    • 153
    • 154
    • 155
    • 156
    • 157
    • 158
    • 159
    • 160
    • 161
    • 162
    • 163
    • 164
    • 165
    • 166
    • 167
    • 168
    • 169
    • 170
    • 171
    • 172
    • 173
    • 174
    • 175
    • 176
    • 177
    • 178
    • 179
    • 180
    • 181
    • 182
    • 183
    • 184
    • 185
    • 186
    • 187
    • 188
    • 189
    • 190
    • 191
    • 192
    • 193
    • 194
    • 195
    • 196
    • 197
    • 198
    • 199
    • 200
    • 201
    • 202
    • 203
    • 204
    • 205
    • 206
    • 207
    • 208
    • 209
    • 210
    • 211
    • 212
    • 213
    • 214
    • 215
    • 216
    • 217
    • 218
    • 219
    • 220
    • 221

    测试上传文件 Controller

    package com.sqm.minio_demo.controller;
     
    import com.sqm.minio_demo.entity.ResultData;
    import com.sqm.minio_demo.entity.UploadResponse;
    import com.sqm.minio_demo.util.MinioUtil;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestParam;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
     
    @RestController
    @Slf4j
    public class TestController {
     
        @Autowired
        private MinioUtil minioUtil;
     
        /**
         * @author: xx
         * @date: 2022/5/25 15:32
         * @description: 上传文件
         */
        @PostMapping("/upload")
        public ResultData minioUpload(@RequestParam(value = "file") MultipartFile file){
            UploadResponse response = null;
            try {
                response = minioUtil.uploadFile(file, "bucket01");
            } catch (Exception e) {
                log.error("上传失败",e);
            }
            return ResultData.ok(response);
        }
     
        /**
         * @author: xx
         * @date: 2022/5/25 15:32
         * @description: 上传视频
         */
        @PostMapping("/uploadVideo")
        public ResultData uploadVideo(@RequestParam(value = "file") MultipartFile file){
            UploadResponse response = null;
            try {
                response = minioUtil.uploadVideo(file, "video-test");
            } catch (Exception e) {
                log.error("上传失败",e);
            }
            return ResultData.ok(response);
        }
     
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
  • 相关阅读:
    WSL2-ubuntu18.04配置笔记1:WSL2-ubuntu系统迁移
    ant-design国际化扩展新语言
    【机器学习】机器学习创建算法第2篇:K-近邻算法【附代码文档】
    Java枚举基本使用,原理探究和扩展
    微信小游戏怎么做?
    Java设计模式——建造者模式
    使用QLoRA对Llama 2进行微调的详细笔记
    给想自学python的同学们提的10条建议
    Net 高级调试之四:Windbg 动态调试
    SystemVerilog Randomization点点滴滴
  • 原文地址:https://blog.csdn.net/qq_37741426/article/details/126846920