• 使用docker创建minio镜像并上传文件,提供demo


    1. 整体描述

    MinIO是一个对象存储解决方案,它提供了一个Amazon Web Services S3兼容的API,并支持所有S3的核心特性。MinIO可以部署在任何地方——公共云或私有云、裸机基础设施、编排环境和边缘基础设施。

    本文档介绍MinIO最新稳定版本RELEASE.2023-09-04T19-57-37Z在Windows平台上部署MinIO的操作、管理和开发。

    上面是官网介绍,用有道翻译的,官网地址: 链接

    2. 环境搭建

    使用docker搭建minio环境,可以直接用官网下载minio镜像,我们先用windows环境搭建一下。

    2.1 windows环境搭建

    在官网下载页面,选择windows,下载的是一个minion.exe文件。我放在了d:/minio目录下,在此再创建一个data文件夹,用来储存minio上传的文件。
    然后在命令行执行如下指令启动minio:

    setx MINIO_ROOT_USER minioadmin
    setx MINIO_ROOT_PASSWORD minioadmin
    D:\minio\minio.exe server D:\minio\data\ --console-address ":9001"
    
    • 1
    • 2
    • 3

    看到如下就说明启动成功了:
    windowsminio启动
    然后通过浏览器访问:
    http://localhost:9001/
    进入如下登录页面:
    默认用户名和密码都是minioadmin
    minio登录页面

    2.2 docker部署

    使用docker部署就更简单了,首先拉取官方镜像:

    docker pull minio/minio
    
    • 1

    拉取成功之后,执行创建和启动容器:

    docker run -p 9000:9000 -p 9001:9001 minio/minio server /data --console-address ":9001"
    
    • 1

    看到如下就说明启动成功:
    docker启动minio
    同样通过浏览器访问:
    http://localhost:9001/
    进入如下登录页面:
    默认用户名和密码都是minioadmin

    3. spring集成

    通过springboot框架集成minio,实现上传文件的目的。

    3.1 添加依赖

    首先创建一个springboot工程,添加如下依赖:

    <!-- lombok 自动生成方法-->
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <version>1.18.24</version>
            </dependency>
            <!--io常用工具类 -->
            <dependency>
                <groupId>commons-io</groupId>
                <artifactId>commons-io</artifactId>
                <version>2.5</version>
            </dependency>
            <!--常用工具类 -->
            <dependency>
                <groupId>org.apache.commons</groupId>
                <artifactId>commons-lang3</artifactId>
            </dependency>
            <!-- minio-->
            <dependency>
                <groupId>io.minio</groupId>
                <artifactId>minio</artifactId>
                <version>8.4.6</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    3.2 配置文件

    在springboot的yml配置文件中添加minio环境的配置:

    #minio相关配置
    minio:
      endpoint: http://127.0.0.1:9000
      accessKey: minioadmin
      secretKey: minioadmin
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3.3 创建config类

    package com.thcb.miniodemo.config;
    
    import io.minio.MinioClient;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.stereotype.Component;
    
    /**
     * MinioUtil
     *
     * @author thcb
     * @date 2023-09-05
     */
    @Component
    public class MinioClientConfig {
        @Value("${minio.endpoint}")
        private String endpoint;
        @Value("${minio.accessKey}")
        private String accessKey;
        @Value("${minio.secretKey}")
        private String secretKey;
    
        /**
         * 注入minio 客户端
         *
         * @return
         */
        @Bean
        public MinioClient minioClient() {
    
            return MinioClient.builder()
                    .endpoint(endpoint)
                    .credentials(accessKey, secretKey)
                    .build();
        }
    
    }
    
    
    • 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

    3.4 创建minio操作类

    minio操作类,用来写minio基础方法

    package com.thcb.miniodemo.minio;
    
    import com.thcb.miniodemo.utils.*;
    import io.minio.*;
    import io.minio.http.Method;
    import io.minio.messages.Bucket;
    import lombok.RequiredArgsConstructor;
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang3.StringUtils;
    import org.springframework.stereotype.Component;
    import org.springframework.util.FastByteArrayOutputStream;
    import org.springframework.web.multipart.MultipartFile;
    
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import java.net.URLEncoder;
    import java.nio.charset.StandardCharsets;
    import java.util.List;
    
    /**
     * MinioUtil
     *
     * @author thcb
     * @date 2023-09-05
     */
    @Component
    @Slf4j
    @RequiredArgsConstructor
    public class MinioUtil {
    
        private final MinioClient minioClient;
        /**
         * 两票数据桶
         */
        public static String TEST_BUCKET = "test";
    
        private static String POLICY = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::%s\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\"],\"Resource\":[\"arn:aws:s3:::%s/*\"]}]}";
    
        public void initMinIo() {
            log.warn("start initMinIo.");
    
            if (!bucketExists(TEST_BUCKET)) {
                log.warn("start makeBucket {}.", TEST_BUCKET);
                makeBucket(TEST_BUCKET);
                log.warn("finish makeBucket {}.", TEST_BUCKET);
            }
            try {
                log.warn("start setBucketPolicy {}.", TEST_BUCKET);
                minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(TEST_BUCKET).config(String.format(POLICY, TEST_BUCKET, TEST_BUCKET)).build());
                log.warn("finish setBucketPolicy {}.", TEST_BUCKET);
            } catch (Exception e) {
                log.error("setBucketPolicy from minio occur exception. e={}", ExceptionUtil.getExceptionMessage(e));
            }
        }
    
        /**
         * 查看存储bucket是否存在
         *
         * @return boolean
         */
        public Boolean bucketExists(String bucketName) {
            Boolean found;
            try {
                found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
            } catch (Exception e) {
                log.error("bucketExists from minio occur exception. e={}", ExceptionUtil.getExceptionMessage(e));
                return false;
            }
            return found;
        }
    
        /**
         * 创建存储bucket
         *
         * @return Boolean
         */
        public synchronized Boolean makeBucket(String bucketName) {
            try {
                minioClient.makeBucket(MakeBucketArgs.builder()
                        .bucket(bucketName)
                        .build());
            } catch (Exception e) {
                log.error("makeBucket from minio occur exception. e={}", ExceptionUtil.getExceptionMessage(e));
                return false;
            }
            return true;
        }
    
        /**
         * 删除存储bucket
         *
         * @return Boolean
         */
        public synchronized Boolean removeBucket(String bucketName) {
            try {
                minioClient.removeBucket(RemoveBucketArgs.builder()
                        .bucket(bucketName)
                        .build());
            } catch (Exception e) {
                log.error("removeBucket from minio occur exception. e={}", ExceptionUtil.getExceptionMessage(e));
                return false;
            }
            return true;
        }
    
        /**
         * 获取全部bucket
         */
        public List<Bucket> getAllBuckets() {
            try {
                List<Bucket> buckets = minioClient.listBuckets();
                return buckets;
            } catch (Exception e) {
                log.error("getAllBuckets from minio occur exception. e={}", ExceptionUtil.getExceptionMessage(e));
            }
            return null;
        }
    
        /**
         * 文件上传
         *
         * @param file       文件
         * @param bucketName 桶名称
         * @return Boolean
         */
        public synchronized String upload2Bucket(MultipartFile file, String bucketName) {
            return String.format("/%s/%s", bucketName, upload(file, bucketName));
        }
    
        /**
         * 文件上传
         *
         * @param file       文件
         * @param bucketName 桶名称
         * @return Boolean
         */
        public synchronized String upload(MultipartFile file, String bucketName) {
            String originalFilename = file.getOriginalFilename();
            if (StringUtils.isBlank(originalFilename)) {
                throw new CustomException("文件格式错误");
            }
            String fileName = UUIDUtil.UUID() + originalFilename.substring(originalFilename.lastIndexOf("."));
            String objectName = DateUtils.dateTimeNow(DateUtils.YYYYMMDD) + "/" + fileName;
            try {
                PutObjectArgs objectArgs = PutObjectArgs.builder().bucket(bucketName).object(objectName)
                        .stream(file.getInputStream(), file.getSize(), -1).contentType(file.getContentType()).build();
                //文件名称相同会覆盖
                minioClient.putObject(objectArgs);
            } catch (Exception e) {
                log.error("upload file to minio occur exception. e={}", ExceptionUtil.getExceptionMessage(e));
                return null;
            }
            return objectName;
        }
    
        /**
         * 文件上传
         *
         * @param file 文件
         * @return Boolean
         */
        public synchronized String upload(MultipartFile file) {
            return upload(file, TEST_BUCKET);
        }
    
        /**
         * 预览
         *
         * @param fileName
         * @return
         */
        public String preview(String fileName) {
            return preview(fileName, TEST_BUCKET);
        }
    
        /**
         * 预览
         *
         * @param fileName
         * @param bucketName 桶名称
         * @return
         */
        public String preview(String fileName, String bucketName) {
            // 查看文件地址
            GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(fileName).method(Method.GET).build();
            try {
                String url = minioClient.getPresignedObjectUrl(build);
                return url;
            } catch (Exception e) {
                log.error("preview file to minio occur exception. e={}", ExceptionUtil.getExceptionMessage(e));
            }
            return null;
        }
    
        /**
         * 文件下载
         *
         * @param fileName         文件名称
         * @param originalFileName 原文件名称
         * @param res              response
         * @return Boolean
         */
        public void download(String fileName, String originalFileName, HttpServletRequest req, HttpServletResponse res) {
            download(fileName, TEST_BUCKET, req, res);
        }
    
        /**
         * 文件下载
         *
         * @param fileName         文件名称
         * @param bucketName       桶名称
         * @param originalFileName 原文件名称
         * @param res              response
         * @return Boolean
         */
        public void download(String fileName, String bucketName, String originalFileName, HttpServletRequest req, HttpServletResponse res) {
            GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketName)
                    .object(fileName).build();
            try (GetObjectResponse response = minioClient.getObject(objectArgs)) {
                byte[] buf = new byte[1024];
                int len;
                try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
                    while ((len = response.read(buf)) != -1) {
                        os.write(buf, 0, len);
                    }
                    os.flush();
                    byte[] bytes = os.toByteArray();
                    res.setCharacterEncoding("utf-8");
    
                    String type = req.getHeader("User-Agent").toLowerCase();
                    String firefox = "firefox", chrome = "chrome";
                    String encodedFileName;
                    if (type.indexOf(firefox) > 0 || type.indexOf(chrome) > 0) {
                        encodedFileName = new String(originalFileName.getBytes(StandardCharsets.UTF_8), "iso8859-1");
                    } else {
                        encodedFileName = URLEncoder.encode(originalFileName, StandardCharsets.UTF_8.name());
                    }
    
                    // 设置强制下载不打开
                    // res.setContentType("application/force-download");
                    // res.setContentType("application/octet-stream");
                    res.setContentType(FileDownloadUtils.getFileContentType(fileName));
                    res.addHeader("Content-Disposition", "attachment;fileName=" + encodedFileName + ";filename*=utf-8''" + encodedFileName);
                    res.addHeader("Content-Length", bytes.length + "");
                    try (ServletOutputStream stream = res.getOutputStream()) {
                        stream.write(bytes);
                        stream.flush();
                    }
                }
                res.flushBuffer();
            } catch (Exception e) {
                log.error("download file from minio occur exception. e={}", ExceptionUtil.getExceptionMessage(e));
            }
        }
    
        /**
         * 删除
         *
         * @param fileName
         * @return
         * @throws Exception
         */
        public boolean remove(String fileName) {
            return remove(fileName, TEST_BUCKET);
        }
    
        /**
         * 删除
         *
         * @param fileName
         * @return
         * @throws Exception
         */
        public synchronized boolean remove(String fileName, String bucketName) {
            try {
                minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
            } catch (Exception e) {
                log.error("remove file from minio occur exception. e={}", ExceptionUtil.getExceptionMessage(e));
                return false;
            }
            return true;
        }
    
    }
    
    
    • 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
    • 222
    • 223
    • 224
    • 225
    • 226
    • 227
    • 228
    • 229
    • 230
    • 231
    • 232
    • 233
    • 234
    • 235
    • 236
    • 237
    • 238
    • 239
    • 240
    • 241
    • 242
    • 243
    • 244
    • 245
    • 246
    • 247
    • 248
    • 249
    • 250
    • 251
    • 252
    • 253
    • 254
    • 255
    • 256
    • 257
    • 258
    • 259
    • 260
    • 261
    • 262
    • 263
    • 264
    • 265
    • 266
    • 267
    • 268
    • 269
    • 270
    • 271
    • 272
    • 273
    • 274
    • 275
    • 276
    • 277
    • 278
    • 279
    • 280
    • 281
    • 282
    • 283
    • 284
    • 285
    • 286

    3.5 创建启动类

    在springboot启动成功后连接minio

    package com.thcb.miniodemo.listener;
    
    import com.thcb.miniodemo.minio.MinioUtil;
    import com.thcb.miniodemo.utils.ExceptionUtil;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.context.event.ApplicationReadyEvent;
    import org.springframework.context.ApplicationListener;
    import org.springframework.stereotype.Component;
    
    /**
     * ApplicationReadyListener
     *
     * @author thcb
     * @date 2023-09-05
     */
    @Component
    public class ApplicationReadyListener implements ApplicationListener<ApplicationReadyEvent> {
        private static final Logger log = LoggerFactory.getLogger(ApplicationReadyListener.class);
    
        @Autowired
        private MinioUtil minioUtil;
    
        @Override
        public void onApplicationEvent(ApplicationReadyEvent event) {
            try {
                doPostConstruct();
            } catch (Exception e) {
                log.error("e={}", ExceptionUtil.getExceptionMessage(e));
            }
        }
    
        private void doPostConstruct() {
            log.info("initMinIo");
            initMinIo();
        }
    
        /**
         * 初始化minio桶文件
         */
        private void initMinIo() {
            minioUtil.initMinIo();
        }
    }
    
    
    • 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

    3.6 测试controller

    写一个测试的controller,测试minio相关功能

    package com.thcb.miniodemo.controller;
    
    import com.thcb.miniodemo.minio.MinioUtil;
    import com.thcb.miniodemo.utils.AjaxResult;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.multipart.MultipartFile;
    
    /**
     * TestController
     *
     * @author thcb
     * @date 2023-09-05
     */
    @RestController
    @RequestMapping("/TestController")
    public class TestController {
    
        private static final Logger log = LoggerFactory.getLogger(TestController.class);
    
        @Autowired
        private MinioUtil minioUtil;
    
        @RequestMapping("/test")
        @ResponseBody
        public String test() {
            return "test success";
        }
    
        @PostMapping("/uploadFile")
        public AjaxResult uploadFile(MultipartFile file) throws Exception {
            try {
                String pathUrl = minioUtil.upload2Bucket(file, MinioUtil.TEST_BUCKET);
                return AjaxResult.success(pathUrl);
            } catch (Exception e) {
                return AjaxResult.error(e.toString());
            }
        }
    }
    
    
    • 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

    4. 测试操作

    4.1 demo运行

    运行工程,如下log说明连接成功:
    miniodemo运行

    4.2 页面查看

    运行完成,在页面应该能看到我们创建的桶:
    桶

    4.3 上传文件

    使用postman调用我们写的测试接口,上传一个文件到minio
    postman调用
    上传成功,在页面上也可以看到这个文件:
    文件查看

    4.4 预览/下载文件

    如果上传的是图片,通过返回的url,浏览器可以直接访问预览,如果是其他类型的文件,是可以通过这个返回的url进行下载的:
    http://localhost:9000/test/20230908/2b31664c4a004a5cb4b2934ebf5300cd.jpg

    5. demo下载

    minio的demo已经上传到csdn,需要的可以自行下载,在本文基本也把主要的核心代码都贴出来了,这个demo使用的是springboot框架,加了一些工具类,可以直接拿来用,或者新工程改个名字就能用了。demo结构截图:
    demo结构
    demo工程地址:demo下载地址,传到了gitcode上,应该是csdn弄的一个代码仓库,和git差不多。

    6. 总结

    minio还是很方便的,从部署到使用,都可以非常快速的搭建,而且比较稳定。

  • 相关阅读:
    智能家居的实用性设置有哪些?智汀小米的怎么样?
    http、https和Cookie
    python IP 端口 socket tcp 介绍
    java第二讲:运算符与流程控制
    密码框验证信息+显示与隐藏 练习
    图解用户登录验证流程,写得太好了!
    刷题记录:牛客NC14704美味菜肴
    作为前端开发,你应该知道的这十几个在线免费工具
    supervisor--go版安装
    单链表(4)
  • 原文地址:https://blog.csdn.net/nhx900317/article/details/132759176