Spring Boot集成MinIo实现文件服务器
本文介绍如何在Spring Boot 3.5.4中集成MinIo 8.5.17实现文件存储功能。主要内容包括:1)引入MinIo和数据库相关依赖;2)配置MinIo连接参数;3)创建文件记录存储表;4)定义文件实体类和业务逻辑;5)配置MinIo客户端;6)实现文件上传模板类。通过Mybatis-Plus管理文件元数据,将文件存储在MinIo对象存储中,同时记录文件信息到MySQL数据库,实现完整
·
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集成的所有内容了,更多细节根据具体业务修改。
更多推荐
所有评论(0)