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

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

  • [1. 整体描述](#1. 整体描述)
  • [2. 环境搭建](#2. 环境搭建)
    • [2.1 windows环境搭建](#2.1 windows环境搭建)
    • [2.2 docker部署](#2.2 docker部署)
  • [3. spring集成](#3. spring集成)
    • [3.1 添加依赖](#3.1 添加依赖)
    • [3.2 配置文件](#3.2 配置文件)
    • [3.3 创建config类](#3.3 创建config类)
    • [3.4 创建minio操作类](#3.4 创建minio操作类)
    • [3.5 创建启动类](#3.5 创建启动类)
    • [3.6 测试controller](#3.6 测试controller)
  • [4. 测试操作](#4. 测试操作)
    • [4.1 demo运行](#4.1 demo运行)
    • [4.2 页面查看](#4.2 页面查看)
    • [4.3 上传文件](#4.3 上传文件)
    • [4.4 预览/下载文件](#4.4 预览/下载文件)
  • [5. demo下载](#5. demo下载)
  • [6. 总结](#6. 总结)

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:

bash 复制代码
setx MINIO_ROOT_USER minioadmin
setx MINIO_ROOT_PASSWORD minioadmin
D:\minio\minio.exe server D:\minio\data\ --console-address ":9001"

看到如下就说明启动成功了:

然后通过浏览器访问:

http://localhost:9001/

进入如下登录页面:

默认用户名和密码都是minioadmin

2.2 docker部署

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

bash 复制代码
docker pull minio/minio

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

bash 复制代码
docker run -p 9000:9000 -p 9001:9001 minio/minio server /data --console-address ":9001"

看到如下就说明启动成功:

同样通过浏览器访问:

http://localhost:9001/

进入如下登录页面:

默认用户名和密码都是minioadmin

3. spring集成

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

3.1 添加依赖

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

bash 复制代码
<!-- 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>

3.2 配置文件

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

bash 复制代码
#minio相关配置
minio:
  endpoint: http://127.0.0.1:9000
  accessKey: minioadmin
  secretKey: minioadmin

3.3 创建config类

java 复制代码
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();
    }

}

3.4 创建minio操作类

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

java 复制代码
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;
    }

}

3.5 创建启动类

在springboot启动成功后连接minio

java 复制代码
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();
    }
}

3.6 测试controller

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

java 复制代码
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());
        }
    }
}

4. 测试操作

4.1 demo运行

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

4.2 页面查看

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

4.3 上传文件

使用postman调用我们写的测试接口,上传一个文件到minio

上传成功,在页面上也可以看到这个文件:

4.4 预览/下载文件

如果上传的是图片,通过返回的url,浏览器可以直接访问预览,如果是其他类型的文件,是可以通过这个返回的url进行下载的:

http://localhost:9000/test/20230908/2b31664c4a004a5cb4b2934ebf5300cd.jpg

5. demo下载

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

demo工程地址:demo下载地址,传到了gitcode上,应该是csdn弄的一个代码仓库,和git差不多。

6. 总结

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

相关推荐
2401_8574396921 分钟前
SpringBoot框架在资产管理中的应用
java·spring boot·后端
怀旧66623 分钟前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
李老头探索24 分钟前
Java面试之Java中实现多线程有几种方法
java·开发语言·面试
芒果披萨30 分钟前
Filter和Listener
java·filter
qq_49244844634 分钟前
Java实现App自动化(Appium Demo)
java
阿华的代码王国43 分钟前
【SpringMVC】——Cookie和Session机制
java·后端·spring·cookie·session·会话
铁锤妹妹头发多1 小时前
新手用docker真**难受
运维·docker·容器
找了一圈尾巴1 小时前
前后端交互通用排序策略
java·交互
龙哥·三年风水3 小时前
群控系统服务端开发模式-应用开发-前端框架
分布式·vue·群控系统
哎呦没4 小时前
SpringBoot框架下的资产管理自动化
java·spring boot·后端