SpringBoot整合Minio

SpringBoot整合Minio

在企业开发中,我们经常会使用到文件存储的业务,Minio就是一个不错的文件存储工具,下面我们来看看如何在SpringBoot中整合Minio

POM

pom文件指定SpringBoot项目所依赖的软件工具包


<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.0modelVersion>
    <groupId>com.zsxqgroupId>
    <artifactId>csg_idc_zsxqartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>csg_idc_zsxqname>
    <description>csg_idc_zsxqdescription>
    <properties>
        <java.version>17java.version>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
        <spring-boot.version>2.7.3spring-boot.version>
    properties>


    <dependencies>
        <dependency>
            <groupId>org.apache.httpcomponentsgroupId>
            <artifactId>httpclientartifactId>
        dependency>

        <dependency>
            <groupId>org.apache.cxfgroupId>
            <artifactId>cxf-rt-transports-httpartifactId>
            <version>3.5.1version>
        dependency>

        <dependency>
            <groupId>org.apache.cxfgroupId>
            <artifactId>cxf-rt-frontend-jaxwsartifactId>
            <version>3.5.1version>
        dependency>

        <dependency>
            <groupId>cn.hutoolgroupId>
            <artifactId>hutool-allartifactId>
            <version>5.8.12version>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redisartifactId>
            <version>2.7.7version>
        dependency>

        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>2.0.16version>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-redis-reactiveartifactId>
        dependency>
        <dependency>
            <groupId>cn.hutoolgroupId>
            <artifactId>hutool-allartifactId>
            <version>5.8.12version>
        dependency>

        <dependency>
            <groupId>org.apache.commonsgroupId>
            <artifactId>commons-lang3artifactId>
            <version>3.12.0version>
        dependency>

        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.26version>
        dependency>

        
        <dependency>
            <groupId>io.miniogroupId>
            <artifactId>minioartifactId>
            <version>8.0.3version>
        dependency>

    dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-dependenciesartifactId>
                <version>${spring-boot.version}version>
                <type>pomtype>
                <scope>importscope>
            dependency>
        dependencies>
    dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.pluginsgroupId>
                <artifactId>maven-compiler-pluginartifactId>
                <version>3.8.1version>
                <configuration>
                    <source>17source>
                    <target>17target>
                    <encoding>UTF-8encoding>
                configuration>
            plugin>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
                <version>${spring-boot.version}version>
                <configuration>
                    <mainClass>com.idc.zsxq.CsgIdcZsxqApplicationmainClass>
                    <skip>trueskip>
                configuration>
                <executions>
                    <execution>
                        <id>repackageid>
                        <goals>
                            <goal>repackagegoal>
                        goals>
                    execution>
                executions>
            plugin>
        plugins>
    build>

project>

YML

SpringBoot配置文件

server:
  port: 11112
# springdoc-openapi项目配置
knife4j:
  enable: true
  setting:
    language: zh_cn
spring:
  redis:
    password:
    host: 127.0.0.1
    port: 6379
    username:
# minio
minio:
  endpoint: http://127.0.0.1:9000
  accessKey: minioadmin
  secretKey: minioadmin
  bucketName: bucket

MinioClientConfig

Minio的配置类

package com.idc.zsxq.config;

import io.minio.MinioClient;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Data
@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();
    }

}

MinioUtil

操作Minio的工具类,实现了判断Bucket是否存在,创建Bucket,上传文件,下载文件等功能

package com.idc.zsxq.util;

import com.idc.zsxq.model.ObjectItem;
import io.minio.*;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @description: minio工具类
 * @version:3.0
 */
@Component
public class MinioUtil {

    @Resource
    private MinioClient minioClient;

    @Value("${minio.bucketName}")
    private String bucketName;

    /**
     * description: 判断bucket是否存在,不存在则创建
     *
     * @return: void
     */
    public void existBucket(String name) {
        try {
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder().bucket(name).build());
            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(name).build());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 创建存储bucket
     *
     * @param bucketName 存储bucket名称
     * @return Boolean
     */
    public Boolean makeBucket(String bucketName) {
        try {
            minioClient.makeBucket(MakeBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * 删除存储bucket
     *
     * @param bucketName 存储bucket名称
     * @return Boolean
     */
    public Boolean removeBucket(String bucketName) {
        try {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                    .bucket(bucketName)
                    .build());
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return true;
    }

    /**
     * description: 上传文件
     *
     * @param multipartFile
     * @return: java.lang.String
     */
    public List<String> upload(MultipartFile[] multipartFile) {
        List<String> names = new ArrayList<>(multipartFile.length);
        for (MultipartFile file : multipartFile) {
            String fileName = file.getOriginalFilename();
            String[] split = fileName.split("\\.");
            if (split.length > 1) {
                fileName = split[0] + "_" + System.currentTimeMillis() + "." + split[1];
            } else {
                fileName = fileName + System.currentTimeMillis();
            }
            InputStream in = null;
            try {
                in = file.getInputStream();
                minioClient.putObject(PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .stream(in, in.available(), -1)
                        .contentType(file.getContentType())
                        .build()
                );
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            names.add(fileName);
        }
        return names;
    }

    /**
     * description: 上传文件流
     *
     * @param in
     * @return: InputStream
     */
    public void upload(InputStream in, String bucketName, String fileName) {
        try {
            minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(fileName)
                    .stream(in, in.available(), -1)
                    .contentType("application/octet-stream;charset=UTF-8")
                    .build()
            );
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * description: 下载文件
     *
     * @param fileName
     * @return: org.springframework.http.ResponseEntity
     */
    public ResponseEntity<byte[]> download(String fileName, String bucketName) {
        ResponseEntity<byte[]> responseEntity = null;
        InputStream in = null;
        ByteArrayOutputStream out = null;
        try {
            in = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
            out = new ByteArrayOutputStream();
            IOUtils.copy(in, out);
            //封装返回值
            byte[] bytes = out.toByteArray();
            HttpHeaders headers = new HttpHeaders();
            try {
                headers.add("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            //需要设置此属性,否则浏览器默认不会读取到响应头中的Accept-Ranges属性,因此会认为服务器端不支持分片,所以会直接全文下载
            headers.add("Access-Control-Expose-Headers", "Accept-Ranges,Content-Range");
            headers.setContentLength(bytes.length);
            headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
            headers.setAccessControlExposeHeaders(Arrays.asList("*"));
            responseEntity = new ResponseEntity<byte[]>(bytes, headers, HttpStatus.OK);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) {
                    try {
                        in.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (out != null) {
                    out.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return responseEntity;
    }

    /**
     * 查看文件对象
     *
     * @param bucketName 存储bucket名称
     * @return 存储bucket内文件对象信息
     */
    public List<ObjectItem> listObjects(String bucketName) {
        Iterable<Result<Item>> results = minioClient.listObjects(
                ListObjectsArgs.builder().bucket(bucketName).build());
        List<ObjectItem> objectItems = new ArrayList<>();
        try {
            for (Result<Item> result : results) {
                Item item = result.get();
                ObjectItem objectItem = new ObjectItem();
                objectItem.setObjectName(item.objectName());
                objectItem.setSize(item.size());
                objectItems.add(objectItem);
            }
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return objectItems;
    }

    /**
     * 批量删除文件对象
     *
     * @param bucketName 存储bucket名称
     * @param objects    对象名称集合
     */
    public Iterable<Result<DeleteError>> removeObjects(String bucketName, List<String> objects) {
        List<DeleteObject> dos = objects.stream().map(e -> new DeleteObject(e)).collect(Collectors.toList());
        Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs
                .builder().bucket(bucketName).objects(dos).build());
        return results;
    }


}


案例

在SpringBoot中如何使用MinioUtil操作Minio,一般我们会使用Minio当做文件缓存

下面这个案例实现了下载文件的功能,逻辑:先从Minio查找是否存在文件,如果存在则从Minio下载文件,如果不存在则从远程文件服务器下载文件

//获取文件
public byte[] downloadFile(String bucketName, String fileId, String newFileId) {

    log.info("下载文件 开始");

    // 测试
    bucketName = "library";
    fileId = "6cc750db08c943b48a259020ce4e6794";
    newFileId = "0fe8cb9eb5954cc2876c3ac8496692a7";

    String fileName = bucketName + "_" + fileId + "_" +
        (newFileId == null ? "" : newFileId);
    log.info("文件名 {}", fileName);

    try {

        //如果没有bucket,则创建
        minioUtil.existBucket(bucketName);

        //判断Minio中是否有该文件
        List<ObjectItem> objectItems = minioUtil.listObjects(bucketName);
        boolean flag = false;
        if (CollectionUtil.isNotEmpty(objectItems)) {
            for (ObjectItem o : objectItems) {
                if (StringUtils.equals(o.getObjectName(), fileName)) {
                    flag = true;
                    break;
                }
            }
        }

        byte[] bytes = null;
        //如果有,则从Minio中获取文件

        if (flag) {
            log.info("从Minio获取文件");
            ResponseEntity<byte[]> download = minioUtil.download(fileName, bucketName);
            bytes = download.getBody();
        } else {
            log.info("从文件服务器获取文件");
            //如果Minio没有,则从文件服务器获取文件,如果报错,则抛出异常
            //封装基本查询参数url
            String withParamsUrl = CnkiUrlEnum.DOWNLOAD_FILE.getUrl() + "?fileId=" + fileId +
                (StringUtils.isEmpty(newFileId) ? "" : "&newFileId=" + newFileId) +
                "&bucketName=" + bucketName;

            HttpResponse httpResponse = HttpRequest.post(withParamsUrl).
                header("accessToken", ConstantEnum.TOKEN.getValue()).body("").execute();

            String status = httpResponse.getStatus() + "";

            //判断是否请求成功
            if (!"200".equals(status) && !"204".equals(status)) {
                log.error("数据库查询异常, CNKI接口返回状态码为:" + status);
                throw new BusinessException(ResultEnum.ERROR);
            }

            //空文件抛出异常
            if (StringUtils.equals(httpResponse.header("Content-Type"), "application/json")) {
                String jsonBody = httpResponse.body();
                JSONObject dataJson = JSONObject.parseObject(jsonBody);
                String dataCode = dataJson.getString("code");
                log.error("文件内容为空 : " + dataCode);
                throw new BusinessException(ResultEnum.ERROR);
            }

            //获取文件字节流
            bytes = httpResponse.bodyBytes();

            //写入Minio
            minioUtil.upload(new ByteArrayInputStream(bytes), bucketName, fileName);
        }

        log.info("下载文件 结束");
        //写到响应流中
        return bytes;
    } catch (Exception e) {
        log.error("下载文件报错 : {}", e);
    }
    return new byte[]{};

}

效果

通过postman调用接口,返回文件流

SpringBoot整合Minio_第1张图片

你可能感兴趣的:(SpringBoot,spring,boot,后端,java)