springboot+minio+docker快速入门

MinIO 是一个基于Apache License v2.0开源协议的对象存储服务。它兼容亚马逊S3云存储服务接口,非常适合于存储大容量非结构化的数据,例如图片、视频、日志文件、备份数据和容器/虚拟机镜像等,而一个对象文件可以是任意大小,从几kb到最大5T不等。
MinIO是一个非常轻量的服务,可以很简单的和其他应用的结合,类似 NodeJS, Redis 或者 MySQL。
Minio提供了非常方便,友好的界面,并且文档也是非常丰富,

快速入门

环境搭建,使用docker镜像快速搭建
docker pull minio/minio
使用docker-compose.yml

version: '3'
services:
   minio:
    image: minio/minio:latest
    container_name: minio
    environment:
      MINIO_ACCESS_KEY: AKIAIOSFODNN7EXAMPLE
      MINIO_SECRET_KEY: wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
    volumes:
    - /mnt/data:/data
    - /mnt/config:/root/.minio
    ports:
      - 9000:9000
    command: server /data
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
      interval: 30s
      timeout: 20s
      retries: 3

docker-compose up -d

登录管理

http://127.0.0.1:9000/minio/login

在这里插入图片描述

输入 MINIO_ACCESS_KEY 和MINIO_SECRET_KEY
在这里插入图片描述

添加 minio

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.15.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>minio</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>minio</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>
        <dependency>
            <groupId>io.minio</groupId>
            <artifactId>minio</artifactId>
            <version>3.0.10</version>
        </dependency>
        <dependency>
            <groupId>org.iherus</groupId>
            <artifactId>qrext4j</artifactId>
            <version>1.3.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

配置


import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 配置属性
 * @author top
 */
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {
    /**
     * 对象存储服务的URL
     */
    private String endpoint;
    /**
     * Access key就像用户ID,可以唯一标识你的账户
     */
    private String accessKey;
    /**
     * Secret key是你账户的密码
     */
    private String secretKey;

    /**
     * 文件桶的名称
     */
    private String bucketName;


    public String getEndpoint() {
        return endpoint;
    }

    public void setEndpoint(String endpoint) {
        this.endpoint = endpoint;
    }

    public String getAccessKey() {
        return accessKey;
    }

    public void setAccessKey(String accessKey) {
        this.accessKey = accessKey;
    }

    public String getSecretKey() {
        return secretKey;
    }

    public void setSecretKey(String secretKey) {
        this.secretKey = secretKey;
    }

    public String getBucketName() {
        return bucketName;
    }

    public void setBucketName(String bucketName) {
        this.bucketName = bucketName;
    }
}


server.port=8789
#文件大小
spring.servlet.multipart.max-file-size=1024MB
spring.servlet.multipart.max-request-size=1024MB

minio.endpoint=http://192.168.0.254:9000
minio.accessKey=AKIAIOSFODNN7EXAMPLE
minio.secretKey=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
minio.bucketName=test


配置类


import io.minio.MinioClient;
import io.minio.errors.InvalidEndpointException;
import io.minio.errors.InvalidPortException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * 
 * 配置类
 * @author top
 */
@Configuration
public class MinioConfig {
    @Autowired
    private MinioProperties properties;

    @Bean
    public MinioClient minioClient() {
        MinioClient minioClient = null;
        try {
            minioClient = new MinioClient(properties.getEndpoint(), properties.getAccessKey(), properties.getSecretKey());
        } catch (InvalidEndpointException | InvalidPortException e) {
            e.printStackTrace();
        }
        return minioClient;
    }
}

封装一个工具,实现文件上传,下载等操作

package com.example.minio.utils;

import com.example.minio.config.MinioProperties;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.Result;
import io.minio.errors.MinioException;
import io.minio.messages.Item;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.iherus.codegen.qrcode.QrcodeConfig;
import org.iherus.codegen.qrcode.SimpleQrcodeGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import org.xmlpull.v1.XmlPullParserException;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

/**
 * @author top
 */
@Component
public class MinioUtils {

    @Autowired
    private MinioProperties properties;
    @Autowired
    private MinioClient minioClient;

    /**
     * 文件上传
     *
     * @param file file
     */
    public void upload(MultipartFile file) {
        try {
            // 使用MinIO服务的URL,端口,Access key和Secret key创建一个MinioClient对象

            // 检查存储桶是否已经存在
            boolean isExist = minioClient.bucketExists(properties.getBucketName());
            if (!isExist) {
                // 创建一个名为test的存储桶,用于存储照片的zip文件。
                minioClient.makeBucket(properties.getBucketName());
            }
            InputStream inputStream = file.getInputStream();
            // 使用putObject上传一个文件到存储桶中。
            minioClient.putObject(properties.getBucketName(), file.getOriginalFilename(), inputStream, inputStream.available(), file.getContentType());
            //关闭
            inputStream.close();
        } catch (MinioException | NoSuchAlgorithmException | IOException | InvalidKeyException | XmlPullParserException e) {
            System.out.println("Error occurred: " + e);
        }
    }

    /**
     * 下载
     *
     * @param response response
     * @param fileName fileName
     */
    public void download(HttpServletResponse response, String fileName) {
        InputStream inputStream = null;
        try {
            ObjectStat stat = minioClient.statObject(properties.getBucketName(), fileName);
            inputStream = minioClient.getObject(properties.getBucketName(), fileName);
            response.setContentType(stat.contentType());
            response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));
            IOUtils.copy(inputStream, response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 获取文件url
     *
     * @param objectName objectName
     * @return url
     */
    public String getObject(String objectName) {
        try {
            return minioClient.getObjectUrl(properties.getBucketName(), objectName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }

    /**
     * 获取所有
     *
     * @return
     */
    public List<Album> list() {
        try {
            List<Album> list = new ArrayList<Album>();
            Iterable<Result<Item>> results = minioClient.listObjects(properties.getBucketName());
            for (Result<Item> result : results) {
                Item item = result.get();
                // Create a new Album Object
                Album album = new Album();
                System.out.println(item.objectName());
                // Set the presigned URL in the album object
                album.setUrl(minioClient.getObjectUrl(properties.getBucketName(), item.objectName()));
                album.setDescription(item.objectName() + "," + item.lastModified() + ",size:" + item.size());
                // Add the album object to the list holding Album objects
                list.add(album);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 文件删除
     *
     * @param name 文件名
     */
    public void delete(String name) {
        try {
            minioClient.removeObject(properties.getBucketName(), name);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 上传生成的二维码
     */
    public void generator() {
        String uuid = UUID.randomUUID().toString();
        InputStream inputStream = bufferedImageToInputStream(qrcode(uuid));
        try {
            minioClient.putObject(properties.getBucketName(), uuid + ".png", bufferedImageToInputStream(qrcode(uuid)), inputStream.available(), MediaType.IMAGE_PNG_VALUE);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public BufferedImage qrcode(String content) {
        QrcodeConfig config = new QrcodeConfig()
                .setBorderSize(2)
                .setPadding(12)
                .setMasterColor("#00BFFF")
                .setLogoBorderColor("#B0C4DE")
                .setHeight(250).setWidth(250);
        return new SimpleQrcodeGenerator(config).setLogo("src/main/resources/logo.png").generate(content).getImage();
    }

    /**
     * @param image image
     * @return InputStream
     */
    public InputStream bufferedImageToInputStream(BufferedImage image) {
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        try {
            ImageIO.write(image, "png", os);
            return new ByteArrayInputStream(os.toByteArray());
        } catch (IOException e) {
            e.fillInStackTrace();
        } finally {
            try {
                os.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    public static class Album {
        private String url;
        private String description;

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }
    }
}

controller


import com.example.minio.utils.MinioUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.util.List;

/**
 * 文件上传下载
 *
 * @author top
 */
@RestController
public class MinioController {
    @Autowired
    private MinioUtils minioUtils;

    @PostMapping(value = "/upload")
    public void upload(@RequestParam("file") MultipartFile file) {
        minioUtils.upload(file);
    }

    @GetMapping(value = "/download")
    public void download(HttpServletResponse response, @RequestParam(value = "fileName") String fileName) throws UnsupportedEncodingException {
        minioUtils.download(response, fileName);
    }

    @GetMapping(value = "/list")
    public List<MinioUtils.Album> list() {
        return minioUtils.list();
    }

    @GetMapping(value = "/objectName")
    public String getObject(@RequestParam(value = "fileName") String fileName) {
        return minioUtils.getObject(fileName);
    }

    @DeleteMapping(value = "/delete/{name}")
    public void delete(@PathVariable String name) {
        minioUtils.delete(name);
    }
}

总结

完成了一个入门的案例,遇到问题可多看看参考文档。
个人博客地址

参考https://docs.min.io/cn/java-client-quickstart-guide.html

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐