小伙伴们好,欢迎关注,一起学习,无限进步
minio 是对象存储服务。它基于 Apache License 开源协议,兼容 Amazon S3 云存储接口。适合存储非结构化数据,如图片,音频,视频,日志等。对象文件最大可以达到 5TB。
优点有高性能,可扩展,操作简单,有图形化操作界面,读写性能优异等。官网
minio 部署可参考这篇:Minio 详细安装部署步骤
SpringBoot 快速整合 minio
1、添加 Maven 依赖
在 pom.xml
文件中添加MinIO客户端依赖项
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.2.2</version>
</dependency>
<!-- 如果 minio 版本是8.3.4以上 okhttp 一定要大于 4.8.1 版本 -->
<!-- <dependency>-->
<!-- <groupId>com.squareup.okhttp3</groupId>-->
<!-- <artifactId>okhttp</artifactId>-->
<!-- <version>4.8.2</version>-->
<!-- </dependency>-->
<!-- 还用到了 fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.78</version>
</dependency>
2、配置MinIO连接信息
在 application.properties
或 application.yml
文件中配置MinIO的连接信息,包括服务器地址、端口、凭据等信息
# Minio 配置
minio.endpoint=127.0.0.1:9000 #对象存储服务的URL
minio.accessKey=admin #Access key账户 写账号也可以
minio.secretKey=admin #Secret key密码
minio.bucketName=test # 桶名称
# 过期时间
minio.expire=7200
# Minio 配置
minio:
endpoint: 127.0.0.1:9000 #对象存储服务的URL
accessKey: admin #Access key账户 写账号也可以
secretKey: admin #Secret key密码
bucketName: test # 桶名称
expire: 7200 # 过期时间
3、创建 MinIO 客户端 Bean
在 SpringBoot 应用的配置类中创建一个 MinIO客户端的 Bean,以便在应用中使用 MinIO 服务
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* minio 配置属性
*/
@Data
@Component
@ConfigurationProperties(prefix = "minio")
public class MinioProperties {
/**
* Minio 连接地址
*/
private String endpoint;
/**
* accessKey 或 账号
*/
private String accessKey;
/**
* secretKey 或 密码
*/
private String secretKey;
/**
* 桶名称
*/
private String bucketName;
/**
* 默认是秒 地址过期时间,设置默认值7200秒
*/
private int expire = 7200;
}
4、异常枚举类
/**
* 异常枚举类
*/
public enum ExceptionEnums {
FILE_NAME_NOT_NULL("0001", "文件名不能为空"),
BUCKET_NAME_NOT_NULL("0002", "桶名称不能为空"),
FILE_NOT_EXIST("0003", "文件不存在"),
BUCKET_NOT_EXIST("0004", "桶不存在"),
BUCKET_NAME_NOT_EXIST("0005", "桶不存在,需要先创建桶在创建文件夹");//枚举类如果写方法的话,此处需要写分号
private String code;
private String msg;
ExceptionEnums(String ecode, String emsg) {
this.code = ecode;
this.msg = emsg;
}
public String getCode() {
return code;
}
public String getMsg() {
return msg;
}
public static ExceptionEnums statOf(String code) {
for (ExceptionEnums state : values())
if (state.getCode().equals(code))
return state;
return null;
}
}
5、全局异常
import org.springframework.http.HttpStatus;
/**
* 异常
*/
public class GeneralException extends RuntimeException {
private Integer errorCode;
public GeneralException() {
}
public GeneralException(Throwable throwable) {
super(throwable);
}
public GeneralException(String msg) {
super(msg);
this.errorCode = HttpStatus.INTERNAL_SERVER_ERROR.value();
}
public GeneralException(Integer errorCode, String msg) {
super(msg);
this.errorCode = errorCode;
}
public Integer getErrorCode() {
return this.errorCode;
}
public void setErrorCode(Integer errorCode) {
this.errorCode = errorCode;
}
}
6、minio 工具类
工具类包含创建 bucket,获取全部 bucket,获取 bucket 文件名和大小列表,文件上传,获取上传文件的完整路径,创建文件夹或目录,判断 bucket 是否存在,判断文件是否存在,文件下载,删除文件,批量删除文件方法文章来源:https://www.toymoban.com/news/detail-859184.html
import com.alibaba.fastjson.JSON;
import io.minio.*;
import io.minio.errors.*;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.apache.tomcat.util.http.fileupload.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.util.*;
/**
* Minio 工具类
*/
@Component
@Slf4j
public class MinioUtils {
@Autowired
private MinioClient minioClient;
@Autowired
private MinioProperties minioProperties;
/**
* 初始化Bucket
*/
private void createBucket(String bucketName) {
try {
// 判断 BucketName 是否存在
if (bucketExists(bucketName)) {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 验证bucketName是否存在
*
* @return boolean true:存在
*/
public boolean bucketExists(String bucketName) {
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
boolean flag = true;
try {
flag = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
e.printStackTrace();
}
return flag;
}
/**
* 获取全部bucket
* <p>
*/
public List<String> getAllBuckets() {
List<String> list = null;
try {
final List<Bucket> buckets = minioClient.listBuckets();
list = new ArrayList<>(buckets.size());
for (Bucket bucket : buckets) {
list.add(bucket.name());
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
/**
* 根据bucketName获取信息
*
* @param bucketName bucket名称
* @return
*/
public String getBucket(String bucketName) throws Exception {
final Optional<Bucket> first = minioClient.listBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
String name = null;
if (first.isPresent()) {
name = first.get().name();
}
return name;
}
/**
* 获取桶中文件名和大小列表
*
* @param bucketName bucket名称
* @param recursive 查询是否递归
* @return
*/
public List<Object> getFileList(String bucketName, boolean recursive) {
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
List<Object> items = new ArrayList<>();
try {
Iterable<Result<Item>> myObjects = minioClient.listObjects(ListObjectsArgs.builder().bucket(bucketName).prefix("/2022-08-03/4674a894-abaf-48cb-9ea9-40a4e8560af9/Desktop").recursive(recursive).build());
Iterator<Result<Item>> iterator = myObjects.iterator();
String format = "{'fileName':'%s','fileSize':'%s'}";
for (Result<Item> myObject : myObjects) {
System.out.println(myObject.get().objectName());
}
while (iterator.hasNext()) {
Item item = iterator.next().get();
items.add(JSON.parse(String.format(format, item.objectName(), formatFileSize(item.size()))));
// items.add(JSON.parse(String.format(format, "/".concat("test").concat("/").concat(item.objectName()), formatFileSize(item.size()))));
}
} catch (Exception e) {
e.printStackTrace();
log.info(e.getMessage());
}
items.remove(0);
return items;
}
/**
* 文件上传
*
* @param file
* @return
*/
public Map<String, Object> uploadFile(String bucketName, MultipartFile[] file) {
if (file == null || file.length == 0) {
throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());
}
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
List<String> orgfileNameList = new ArrayList<>(file.length);
for (MultipartFile multipartFile : file) {
String orgfileName = multipartFile.getOriginalFilename();
orgfileNameList.add(orgfileName);
try {
//文件上传
InputStream in = multipartFile.getInputStream();
minioClient.putObject(PutObjectArgs.builder().bucket(bucketName).object(orgfileName).stream(in, multipartFile.getSize(), -1).contentType(multipartFile.getContentType()).build());
in.close();
} catch (Exception e) {
e.printStackTrace();
log.error(e.getMessage());
}
}
Map<String, Object> data = new HashMap<>();
data.put("bucketName", bucketName);
data.put("fileName", orgfileNameList);
return data;
}
/**
* 获取上传文件的完整路径
*
* @param bucketName 桶名称
* @param fileName 文件名
* @param expire 地址过期时间
* @return
*/
public String getPresignedObjectUrl(String bucketName, String fileName, Integer expire) {
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
if (StringUtils.isEmpty(fileName)) {
throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());
}
expire = Objects.isNull(expire) ? minioProperties.getExpire() : expire;
// 验证桶是否存在在
final boolean validationBucket = bucketExists(bucketName);
if (!validationBucket) {
throw new GeneralException(ExceptionEnums.BUCKET_NOT_EXIST.getMsg());
}
// 验证文件是否存在
final boolean validationFileName = doFileNameExist(bucketName, fileName);
if (!validationFileName) {
throw new GeneralException(ExceptionEnums.FILE_NOT_EXIST.getMsg());
}
String url = null;
try {
// 获取桶和文件的完整路径
url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder().bucket(bucketName).object(fileName).method(Method.GET).expiry(expire).build());
} catch (MinioException e) {
log.error("Error occurred: " + e);
} catch (Exception e) {
e.printStackTrace();
}
return url;
}
/**
* 创建文件夹或目录
*
* @param bucketName 存储桶
* @param objectName 目录路径
*/
public Map<String, String> putDirObject(String bucketName, String objectName) throws IOException, InvalidKeyException, InvalidResponseException, InsufficientDataException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, ErrorResponseException {
// 判断桶是否存在
if (!bucketExists(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_EXIST.getMsg());
}
final ObjectWriteResponse response = minioClient.putObject(
PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(
new ByteArrayInputStream(new byte[]{}), 0, -1)
.build());
Map<String, String> map = new HashMap<>();
map.put("etag", response.etag());
map.put("versionId", response.versionId());
return map;
}
/**
* 判断桶是否存在
*
* @param bucketName 存储桶
* @param objectName 文件夹名称(去掉/)
* @return true:存在
*/
public boolean doFolderExist(String bucketName, String objectName) {
boolean exist = false;
try {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).prefix(objectName).recursive(false).build());
for (Result<Item> result : results) {
Item item = result.get();
if (item.isDir()) {
exist = true;
}
}
} catch (Exception e) {
exist = false;
}
return exist;
}
/**
* 判断文件是否存在
*
* @param fileName 对象
* @return true:存在
*/
public boolean doFileNameExist(String bucketName, String fileName) {
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
if (StringUtils.isEmpty(fileName)) {
throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());
}
boolean exist = true;
try {
minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
} catch (Exception e) {
exist = false;
}
return exist;
}
/**
* 文件下载
*
* @param response
* @param fileName
*/
public void downloadFile(HttpServletResponse response, String bucketName, String fileName) {
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
if (StringUtils.isEmpty(fileName)) {
throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());
}
// 判断文件是否存在
final boolean flag = doFileNameExist(bucketName, fileName);
if (!flag) {
throw new GeneralException(ExceptionEnums.FILE_NOT_EXIST.getMsg());
}
InputStream in = null;
try {
// 获取对象信息
StatObjectResponse stat = minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(fileName).build());
response.setContentType(stat.contentType());
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
//文件下载
in = minioClient.getObject(
GetObjectArgs.builder().bucket(bucketName).object(fileName).build());
IOUtils.copy(in, response.getOutputStream());
} catch (Exception e) {
log.error(e.getMessage());
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
log.error(e.getMessage());
}
}
}
}
/**
* 删除文件
*
* @param bucketName bucket名称
* @param fileName 文件名称
* 说明:当前方法不能真正删除,需要验证
*/
public void deleteFile(String bucketName, String fileName) {
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
if (StringUtils.isEmpty(fileName)) {
throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());
}
try {
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(fileName).build());
} catch (Exception e) {
log.error(e.getMessage());
e.printStackTrace();
}
}
/**
* 批量文件删除
*
* @param bucketName bucket名称
* @param fileNames 文件名
*/
public void deleteBatchFile(String bucketName, List<String> fileNames) {
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
if (CollectionUtils.isEmpty(fileNames)) {
throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());
}
try {
List<DeleteObject> objects = new LinkedList<>();
for (String fileName : fileNames) {
objects.add(new DeleteObject(fileName));
}
Iterable<Result<DeleteError>> results =
minioClient.removeObjects(
RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build());
for (Result<DeleteError> result : results) {
DeleteError error = result.get();
log.error("Error occurred: " + error);
}
} catch (Exception e) {
e.printStackTrace();
log.error("批量删除失败!error:{}", e);
}
}
/**
* 文件大小
*
* @param fileS
* @return
*/
private static String formatFileSize(long fileS) {
DecimalFormat df = new DecimalFormat("#.00");
String fileSizeString = "";
String wrongSize = "0B";
if (fileS == 0) {
return wrongSize;
}
if (fileS < 1024) {
fileSizeString = df.format((double) fileS) + " B";
} else if (fileS < 1048576) {
fileSizeString = df.format((double) fileS / 1024) + " KB";
} else if (fileS < 1073741824) {
fileSizeString = df.format((double) fileS / 1048576) + " MB";
} else {
fileSizeString = df.format((double) fileS / 1073741824) + " GB";
}
return fileSizeString;
}
}
7、文件调用接口
上传文件,获取上传文件完整路径,文件下载,文件删除,批量删除文章来源地址https://www.toymoban.com/news/detail-859184.html
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.Map;
@RestController
public class MinioController {
@Autowired
private MinioUtils minioUtils;
/**
* 获取桶中文件名和大小列表
*
* @return
*/
@GetMapping("/getFileList")
public List<Object> getFileList() {
return minioUtils.getFileList("test", true);
}
/**
* 判断文件是否存在
*
* @param bucketName
* @param fileName
* @return
*/
@GetMapping("/doFileNameExist")
public boolean doFileNameExist(String bucketName, String fileName) {
return minioUtils.doFolderExist(bucketName, fileName);
}
/**
* 上传文件
*
* @param file
* @return
*/
@PostMapping("/uploadFiles")
public Map<String, Object> uploadFiles(String bucketName, @RequestParam(name = "file", required = false) MultipartFile[] file) {
if (file == null || file.length == 0) {
throw new GeneralException(ExceptionEnums.FILE_NAME_NOT_NULL.getMsg());
}
if (StringUtils.isEmpty(bucketName)) {
throw new GeneralException(ExceptionEnums.BUCKET_NAME_NOT_NULL.getMsg());
}
return minioUtils.uploadFile(bucketName, file);
}
/**
* 获取上传文件的完整浏览路径
*
* @param filename
* @return
*/
@GetMapping("/getPresignedObjectUrl")
public String getPresignedObjectUrl(@RequestParam(name = "filename") String filename) {
return minioUtils.getPresignedObjectUrl("test", filename, null);
}
/**
* 文件下载
*
* @param response
* @param fileName
*/
@GetMapping("/downloadFile")
public void downloadFile(HttpServletResponse response, @RequestParam("fileName") String fileName) {
minioUtils.downloadFile(response, "test", fileName);
}
/**
* 删除单个文件
*
* @param fileName 完整路径(不包含bucket)
*/
@DeleteMapping("/deleteFile")
public void deleteFile(String bucketName, String fileName) {
minioUtils.deleteFile(bucketName, fileName);
}
/**
* 批量删除文件
*
* @param fileNames 完整路径(不包含bucket)
*/
@DeleteMapping("/deleteBatchFile")
public void deleteBatchFile(String bucketName, @RequestParam("fileNames") List<String> fileNames) {
minioUtils.deleteBatchFile(bucketName, fileNames);
}
}
到了这里,关于SpringBoot整合Minio的详细步骤的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!