Spring Boot集成MinIo实现文件服务器

本文将简述如何将MinIo和Spring Boot集成,并使用Mybatis-Plus实现文件记录存储。
JDK 版本:17.0.12
SpringBoot 版本:3.5.4
MiniIo 版本:8.5.17
数据库:mysql 8.0.39
MinIo 官方文档地址:https://docs.min.io/community

在这里插入图片描述

1.引入依赖

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.32</version>
        </dependency>
        <dependency>
        <!--官方文档推荐SDK版本 -->
        <groupId>io.minio</groupId>
        <artifactId>minio</artifactId>
        <version>8.5.17</version>
       </dependency>
        <!-- minio8.3以上版本需要手动引入okhttp-->
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>4.9.3</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-spring-boot3-starter</artifactId>
            <version>3.5.10</version>
        </dependency>
    </dependencies>

2.修改配置文件

application.yml

spring:
  application:
     name: MinioProject
  datasource: #数据库连接
    url: jdbc:mysql://192.168.48.164:3306/db01?characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
  mapper-locations: classpath*:mapper/**/*.xml
  type-aliases-package: com.example.entity
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
minio:
  url: http://localhost:9000 #配置minIO 地址
  access-key: minio_admin #登录的用户名
  secret-key: minio_admin #登录的密码
  bucket-name: testbucket #默认bucket的名称

3.创建数据库表

数据库表用于存放文件记录

DROP TABLE IF EXISTS `system_file`;
CREATE TABLE `system_file`  (
  `file_id` bigint NOT NULL,
  `bucket_name` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `file_name` varchar(1000) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,
  `upload_date` timestamp NULL DEFAULT NULL,
  PRIMARY KEY (`file_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

4.定义文件存储相关实体、业务类

实体类:

@TableName(value = "system_file")
@Data
public class SystemFile {
    @TableId(type=IdType.ASSIGN_ID)
    private Long fileId;
    private String bucketName;
    @TableField(value = "file_name")
    private String myFileName;
    private Date uploadDate;

}

Mapper:

@Mapper
public interface SystemFileMapper extends BaseMapper<SystemFile> {
}

Service业务类:
业务接口:

package com.example.minioproject.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.example.minioproject.entity.SystemFile;

public interface SystemFileService extends IService<SystemFile>  {
}

实现类:

@Service
public class SystemFileServiceImpl extends ServiceImpl<SystemFileMapper, SystemFile> implements SystemFileService {
}

5.定义配置类

@Configuration
public class MinioConfig {
    @Value("${minio.url}")
    private String endpoint;
    @Value("${minio.access-key}")
    private String accessKey;
    @Value("${minio.secret-key}")
    private String secretKey;
    @Bean
    public MinioClient minioClient(){
        return MinioClient.builder()
                .endpoint(endpoint)
                .credentials(accessKey,secretKey)
                .build();
    }
}

6.定义MinIo 模板

package com.example.minioproject.config;

import com.example.minioproject.entity.SystemFile;
import com.example.minioproject.service.SystemFileService;
import io.minio.*;
import io.minio.http.Method;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

import java.io.InputStream;
import java.util.Date;

/**
 * @ClassName:MinioTemplate
 * @Author: ciku
 * @Date: 2025/8/13 20:14
 * @Description: Minio文件上传模板
 */
@Configuration
@Component
public class MinioTemplate {
    public  MinioClient minioClient;
    public SystemFileService systemFileService;
    private static final Logger logger = LoggerFactory.getLogger(MinioTemplate.class);
    public MinioTemplate(MinioClient minioClient,SystemFileService systemFileService) {
        this.minioClient=minioClient;
        this.systemFileService=systemFileService;
    }

    /**
     * 新增bucket
     * @param bucketName
     * @throws Exception
     */
    public void creatBucket(String bucketName) throws Exception{
        boolean found =
                minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (!found) {
            minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
        } else {
            logger.info("Bucket {} already exists.",bucketName);
        }
    }

    /**
     * 删除bukect
     * @param bucketName
     * @throws Exception
     */
    public void removeBucket(String bucketName) throws Exception{
        boolean found =
                minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
        if (!found) {
            logger.info("Bucket {} not exists.",bucketName);
        } else {
            minioClient.removeBucket(RemoveBucketArgs.builder()
                            .bucket(bucketName)
                            .build());
            logger.info("删除bucket{}",bucketName);
        }
    }

    /**
     * 上传文件
     * @param bucketName bucket名称
     * @param fileName 文件明
     * @param filePath 文件路径
     * @throws Exception
     */
    public long uploadFileByPath(String bucketName, String fileName, String filePath) throws Exception {
        creatBucket(bucketName);
        minioClient.uploadObject(
                UploadObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .filename(filePath)
                        .build());
        SystemFile systemFile = new SystemFile();
        systemFile.setMyFileName(fileName);
        systemFile.setUploadDate(new Date());
        systemFile.setBucketName(bucketName);
        systemFileService.save(systemFile);
        logger.info(fileName+"is successfully uploaded  to bucket{}.",bucketName);
        return systemFile.getFileId();

    }

    /**
     * 下载文件
     * @param bucketName bucket名称
     * @param fileName 文件名
     * @return
     * @throws Exception
     */

    public InputStream downloadFile(String bucketName,String fileName)throws Exception{
        return minioClient.getObject(
                GetObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .build()
        );
    }

    /**
     * 上传文件(文件流)
     * @param bucketName
     * @param fileName
     * @param inputStream
     * @throws Exception
     */
    public long uploadFileByStream(String bucketName, String fileName, InputStream inputStream) throws Exception{
        this.creatBucket(bucketName);
        minioClient.putObject(
                PutObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                        .stream(inputStream,inputStream.available(),-1)
                        .build()
        );
        SystemFile systemFile = new SystemFile();
        systemFile.setMyFileName(fileName);
        systemFile.setUploadDate(new Date());
        systemFile.setBucketName(bucketName);
        systemFileService.save(systemFile);
        logger.info(fileName+"is successfully uploaded  to bucket{}.",bucketName);
        return systemFile.getFileId();

    }

    /**
     * 获取文件url
     * @param bucketName
     * @param fileName
     * @return
     * @throws Exception
     */
    public String getFileUrl(String bucketName,String fileName) throws Exception{
        return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                        .method(Method.GET)
                        .bucket(bucketName)
                        .object(fileName)
                        .build()
        );
    }

    /**
     *  删除文件
      * @param bucketName
     * @param fileName
     * @throws Exception
     */
    public void removeFile(String bucketName,String fileName) throws Exception{
        minioClient.removeObject(RemoveObjectArgs.builder()
                        .bucket(bucketName)
                        .object(fileName)
                .build());
    }


}

7.MinIo Service类

MinioService 接口:

package com.example.minioproject.service;

import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.util.Map;

public interface MinioService {
    /**
     * 获取上传文件地址
     * @param bucketName
     * @param fileName
     * @return
     */
    String getFileUrl(String bucketName,String fileName);

    /**
     * 上传文件
     * @param fileName
     * @param bucketName
     * @param inputStream
     * @return
     */
    Long uploadFile(String bucketName,String fileName, InputStream inputStream);

    /**
     * 上传多个文件
     * @param bucketName
     * @param fileMap
     */
    String uploadFiles(String bucketName, Map<String, MultipartFile> fileMap);

    /**
     * 下载文件
     * @param fileId 通过上传成功返回的文件id
     * @return
     */
    InputStream downloadFile(Long fileId);

    /**
     * 删除Bucket
     * @param bucketName
     */
    void removeBucket(String bucketName);
}

MinioService 实现类:

package com.example.minioproject.service.Impl;

import com.example.minioproject.config.MinioTemplate;
import com.example.minioproject.entity.SystemFile;
import com.example.minioproject.service.MinioService;
import com.example.minioproject.service.SystemFileService;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.util.Map;
import java.util.StringJoiner;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @ClassName:MinioServiceImpl
 * @Author: ciku
 * @Date: 2025/8/13 21:29
 * @Description: MinioServiceImpl 业务实现类
 */
@Service
public class MinioServiceImpl implements MinioService {

    @Autowired
    private MinioTemplate minioTemplate;
    @Autowired
    private SystemFileService fileService;
    @Value("${minio.bucket-name}")
    private String bucketName;
    private static final Logger logger = LoggerFactory.getLogger(MinioTemplate.class);

    @Override
    public String getFileUrl( String bucketName,String fileName) {
        try {
            if(StringUtils.isEmpty(bucketName)){
                bucketName=this.bucketName;
            }
            return minioTemplate.getFileUrl(bucketName,fileName);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Long uploadFile(String bucketName,String fileName,  InputStream inputStream) {

        try {
            if(StringUtils.isEmpty(bucketName)){
                bucketName=this.bucketName;
            }
             return minioTemplate.uploadFileByStream(bucketName,fileName,inputStream);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String uploadFiles(String bucketName, Map<String, MultipartFile> fileMap) {
        ExecutorService service = Executors.newFixedThreadPool(8);
        StringJoiner ids = new StringJoiner(",");
        if(fileMap!=null){
                for(String key:fileMap.keySet()){
                    service.submit(()->{
                        try {
                            Long fileId=minioTemplate.uploadFileByStream(bucketName,key,fileMap.get(key).getInputStream());
                            if(fileId!=0L){
                                ids.add(fileId.toString());
                            }
                        } catch (Exception e) {
                            throw new RuntimeException(e);
                        }
                    });
                }

        }
        return ids.toString();
    }

    @Override
    public InputStream downloadFile(Long fileId) {
        SystemFile systemFile = fileService.getById(fileId);
        String fileName="";
        String getBucketName="";
        if(systemFile!=null){
            fileName=systemFile.getMyFileName();
            bucketName=systemFile.getBucketName();
        }
        if(StringUtils.isEmpty(fileName)){
            logger.info("fileName is Empty!");
            return null;
        }
        if(StringUtils.isEmpty(bucketName)){
            bucketName=this.bucketName;
        }
        try {
            return minioTemplate.downloadFile(bucketName,fileName);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void removeBucket(String bucketName) {
        try {
            minioTemplate.removeBucket(bucketName);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

8.定义Contoller类

package com.example.minioproject.controller;

import com.example.minioproject.service.MinioService;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @ClassName:MinioController
 * @Author: ciku
 * @Date: 2025/8/13 23:25
 * @Description: 必须描述类做什么事情, 实现什么功能
 */
@RestController
@RequestMapping("/file")
public class MinioController
{
    @Autowired
    private MinioService minioService;

    @PostMapping("/uploadFile")
    public void uploadFile(@RequestParam("file")MultipartFile file,@RequestParam(value = "bucketName",required = false) String bucketName){
        if(file!=null){
            String filename = file.getOriginalFilename();
            try {
                minioService.uploadFile(bucketName,filename,file.getInputStream());
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }
    @PostMapping("/uploadFiles")
    public void uploadFile(@RequestParam("files")List<MultipartFile> files,@RequestParam(value = "bucketName",required = false) String bucketName){
        if(!CollectionUtils.isEmpty(files)){
            Map<String,MultipartFile> fileMap=new HashMap<>();
                files.stream().forEach(part->{
                    fileMap.put(part.getOriginalFilename(),part);
                });
                minioService.uploadFiles(bucketName,fileMap);
        }
    }

    @GetMapping("/downloadFile")
    public void downloadFile(@RequestParam("fileId") String fileId,@RequestParam("fileName")String fileName, HttpServletResponse response){
        response.setContentType( "application/octet-stream");
        response.setHeader("Content-Disposition", "attachment; filename="+fileName);
        OutputStream out = null;
        try (InputStream inputStream = minioService.downloadFile(Long.valueOf(fileId));){
            out = response.getOutputStream();
                byte[] buffer = new byte[4096];
                int length;
                while ((length = inputStream.read(buffer)) > 0) {
                    out.write(buffer, 0, length);
                }
        }catch (IOException e){
            e.printStackTrace();
        }

    }
}

8.文件上传

由于博主本地postman出了点问题,使用SpringBootTest测试,由于代码中已经判断了如果bucket不存在,则创建,所以无需手动创建。bucketName为空时使用配置文件的默认bucket.

    @Test
    public void uploadStreamFile() throws Exception {
        File file = new File("F:\\minioTest\\量化级软件工程化系统项目团队绩效域管理实践.docx");
        FileInputStream stream = new FileInputStream(file);
        long start = System.currentTimeMillis();
        minioService.uploadFile("mybucket","量化级软件工程化系统项目团队绩效域管理实践.docx",stream);
        long end = System.currentTimeMillis();
        System.out.println("上传文件成功,花费"+(end-start)+"ms");
    }

在这里插入图片描述
数据库记录:
在这里插入图片描述

9.文件下载

在这里插入图片描述
以上就是springBoot集成的所有内容了,更多细节根据具体业务修改。

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐