本文介绍SpringBoot如何整合Minio,解决文件存储问题
文章目录
- 前言
- 环境搭建
- 项目环境搭建
- 添加依赖库
- yml配置
- Docker安装minio
- 代码实现
- MiniConfig
- service
- controller
- 测试
前言
参考链接:
- 官网
环境搭建
项目环境搭建
将minio单独封装成一个module,上层导入依赖,比如api module
在api 模块,测试接口
添加依赖库
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-learn</artifactId>
<groupId>org.ym</groupId>
<version>1.0-SNAPSHOT</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>learn-common-minio</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.4.5</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
</dependencies>
</project>
yml配置
application.yml配置
,配置minio基本信息
server:
port: 9203
minio:
endpoint: http://127.0.0.1:9000
bucketName: learn-minio
accessKey: minioadmin
secretKey: minioadmin
spring:
application:
name: learn-api
mvc:
pathmatch:
matching-strategy: ant_path_matcher
Docker安装minio
首先要创建本机data目录/Users/yangmiao/config/docker/data/minio
,至于Docker常见命令的使用,参考我的另一篇文章Docker常见命令使用
sudo docker run -p 9000:9000 -p 9090:9090 \
--name minio \
-d --restart=always \
-e "MINIO_ACCESS_KEY=minioadmin" \
-e "MINIO_SECRET_KEY=minioadmin" \
-v /Users/yangmiao/config/docker/data/minio:/data \
minio/minio server \
/data --console-address ":9090" -address ":9000"
镜像启动成功后,浏览器访问:http://localhost:9090/
,输入用户名和密码minioadmin。
代码实现
MiniConfig
package com.ym.learn.minio.config;
import com.ym.learn.minio.service.MinioService;
import com.ym.learn.minio.service.impl.MinioServiceImpl;
import io.minio.MinioClient;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @Author: Yangmiao
* @Date: 2023/4/21 23:04
* @Desc: minio基础配置
*/
@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {
/**
* 服务地址
*/
private String endpoint;
/**
* 访问key
*/
private String accessKey;
/**
* 密钥
*/
private String secretKey;
/**
* 桶名称
*/
private String bucketName;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
}
@Bean
public MinioService minioService(){
return new MinioServiceImpl();
}
}
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.ym.learn.minio.config.MinioConfig
service
package com.ym.learn.minio.service;
import com.ym.learn.minio.config.MinioConfig;
import io.minio.MinioClient;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* @Author: Yangmiao
* @Date: 2023/4/21 23:09
* @Desc: minio服务
*/
public interface MinioService {
/**
* 判断bucket是否存在
* @param bucketName
* @return
*/
boolean bucketExists(String bucketName);
/**
* 创建bucket
* @param bucketName
* @return
*/
boolean createBucket(String bucketName);
/**
* 删除bucket
* @param bucketName
* @return
*/
boolean removeBucket(String bucketName);
/**
* 获取所有的bucket
* @return
*/
List<Bucket> getAllBuckets();
/**
* 上传文件
* @param multipartFile
* @return
*/
String upload(MultipartFile multipartFile);
/**
* 下载文件
* @param fileName
* @param res
*/
void download(String fileName, HttpServletResponse res);
/**
* 删除文件
* @param fileName
* @return
*/
boolean remove(String fileName);
/**
* 预览文件
* @param fileName
* @return
*/
String preview(String fileName);
/**
* 查看所有的文件对象
* @return
*/
List<Item> getAllFiles();
}
package com.ym.learn.minio.service.impl;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.StrUtil;
import com.ym.learn.minio.config.MinioConfig;
import com.ym.learn.minio.service.MinioService;
import io.minio.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: Yangmiao
* @Date: 2023/4/21 23:12
* @Desc:
*/
@Service
@Slf4j
public class MinioServiceImpl implements MinioService {
@Autowired
private MinioConfig minioConfig;
@Autowired
private MinioClient minioClient;
@Override
public boolean bucketExists(String bucketName) {
if (StrUtil.isEmpty(bucketName)){
return false;
}
boolean res = false;
try {
res = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
log.error("bucketExists error: {}",e.getMessage());
e.printStackTrace();
return false;
}
return res;
}
@Override
public boolean createBucket(String bucketName) {
if (StrUtil.isEmpty(bucketName)){
return false;
}
try {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}catch (Exception e){
log.error("createBucket error: {}",e.getMessage());
e.printStackTrace();
return false;
}
return true;
}
@Override
public boolean removeBucket(String bucketName) {
if (StrUtil.isEmpty(bucketName)){
return false;
}
try {
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
}catch (Exception e){
log.error("removeBucket error: {}",e.getMessage());
e.printStackTrace();
return false;
}
return true;
}
@Override
public List<Bucket> getAllBuckets() {
try {
return minioClient.listBuckets();
}catch (Exception e){
log.error("getAllBuckets error: {}",e.getMessage());
e.printStackTrace();
}
return null;
}
@Override
public String upload(MultipartFile multipartFile) {
if (multipartFile == null){
return null;
}
String fileName = multipartFile.getOriginalFilename();
if (StrUtil.isBlank(fileName)){
throw new RuntimeException("upload fail!");
}
fileName = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))+"_"+UUID.randomUUID().toString().replaceAll("-","")+
fileName.substring(fileName.lastIndexOf("."));
log.debug("upload: fileName {}",fileName);
try {
minioClient.putObject(PutObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(fileName)
.stream(multipartFile.getInputStream(), multipartFile.getSize(), -1)
.contentType(multipartFile.getContentType())
.build());
} catch (Exception e) {
log.error("upload error: {}",e.getMessage());
e.printStackTrace();
return null;
}
String url = minioConfig.getEndpoint()+"/"+minioConfig.getBucketName()+"/"+fileName;
return url;
}
@Override
public void download(String fileName, HttpServletResponse res) {
if (StrUtil.isEmpty(fileName)){
return;
}
ByteArrayOutputStream out = null;
try {
GetObjectResponse objectResponse = minioClient.getObject(GetObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(fileName)
.build());
out = new ByteArrayOutputStream();
IoUtil.copy(objectResponse,out);
res.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
res.setContentLength(out.toByteArray().length);
res.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
try(ServletOutputStream stream = res.getOutputStream()){
stream.write(out.toByteArray());
stream.flush();
}
}catch (Exception e){
log.error("download error: {}",e.getMessage());
e.printStackTrace();
}finally {
if (out != null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
public boolean remove(String fileName) {
if (StrUtil.isEmpty(fileName)){
return false;
}
try {
minioClient.removeObject(RemoveObjectArgs.builder().bucket(minioConfig.getBucketName()).object(fileName).build());
}catch (Exception e){
log.error("remove error: {}",e.getMessage());
e.printStackTrace();
return false;
}
return true;
}
@Override
public String preview(String fileName) {
if (StrUtil.isEmpty(fileName)){
return null;
}
try {
return minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.bucket(minioConfig.getBucketName())
.object(fileName)
.method(Method.GET)
.build());
}catch (Exception e){
log.error("preview error: {}",e.getMessage());
e.printStackTrace();
return null;
}
}
@Override
public List<Item> getAllFiles() {
List<Item> itemList = new ArrayList<>();
try {
Iterable<Result<Item>> listObjects = minioClient.listObjects(
ListObjectsArgs.builder()
.bucket(minioConfig.getBucketName())
.build());
try {
for (Result<Item> item:listObjects){
itemList.add(item.get());
}
}catch (Exception e){
log.error("getAllFiles error, get item fail! {}",e.getMessage());
e.printStackTrace();
return null;
}
}catch (Exception e){
log.error("getAllFiles error {}",e.getMessage());
e.printStackTrace();
return null;
}
return itemList;
}
}
controller
package com.ym.learn.api.controller;
import com.ym.learn.core.api.R;
import com.ym.learn.minio.config.MinioConfig;
import com.ym.learn.minio.service.MinioService;
import io.minio.messages.Bucket;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
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.util.List;
/**
* @Author: Yangmiao
* @Date: 2023/4/22 23:00
* @Desc:
*/
@Api(tags = "minio接口")
@RestController
@RequestMapping("/file")
@Slf4j
public class MinioController {
@Autowired
private MinioService minioService;
@Autowired
private MinioConfig minioConfig;
@ApiOperation(value = "判断bucket是否存在")
@GetMapping("/bucketExist")
public R bucketExist(@RequestParam("bucketName")String bucketName){
boolean exists = minioService.bucketExists(bucketName);
return R.ok(exists);
}
@ApiOperation(value = "创建bucket")
@GetMapping("/createBucket")
public R createBucket(String bucketName){
boolean ret = minioService.createBucket(bucketName);
return R.ok(ret);
}
@ApiOperation(value = "删除bucket")
@GetMapping("/removeBucket")
public R removeBucket(String bucketName){
boolean removeBucket = minioService.removeBucket(bucketName);
return R.ok(removeBucket);
}
@ApiOperation(value = "获取所有的bucket")
@GetMapping("/getAllBucket")
public R getAllBucket(){
List<Bucket> allBuckets = minioService.getAllBuckets();
return R.ok(allBuckets);
}
@ApiOperation(value = "上传文件")
@PostMapping("/uploadFile")
public R uploadFile(@RequestParam("file") MultipartFile file){
String upload = minioService.upload(file);
return R.ok(upload);
}
@ApiOperation(value = "查看Image")
@GetMapping("/preview")
public R preview(String fileName){
String preview = minioService.preview(fileName);
return R.ok(preview);
}
@ApiOperation(value = "下载文件")
@GetMapping("/downloadFile")
public R downloadFile(String fileName, HttpServletResponse response){
minioService.download(fileName,response);
return R.ok();
}
@ApiOperation(value = "删除文件")
@PostMapping("/removeFile")
public R removeFile(String url){
String objName = url.substring(url.lastIndexOf(minioConfig.getBucketName()+"/") + minioConfig.getBucketName().length()+1);
boolean remove = minioService.remove(objName);
return R.ok(remove);
}![在这里插入图片描述](https://img-blog.csdnimg.cn/f9965e88a5b84d62ba029ffc86e065f3.jpeg#pic_center)
}
测试
由于项目已引入了Knife4j,本文采用了Knife4j和ApiPost测试
查看minio是否上传成功图片,以及bucket是否成功创建。