springboot集成Minio
1、Minio单击安装
mkdir /data/minio cd /data/minio chmod +x minio mkdir data mkdir logs wget https://dl.min.io/server/minio/release/linux-amd64/minio
2、将Minio的账密写入环境变量中
vim /etc/profile export MINIO_ROOT_USER=username export MINIO_ROOT_PASSWORD=password source /etc/profile
3、启动Minio(9002-server端口,9001-web端口)
nohup ./minio server --address :9002 --console-address :9001 /data/minio/data > /data/minio/logs/minio.log 2>&1 &
4、Springboot依赖
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>7.1.0</version>
</dependency>
5、yml配置
server:
port: 8084
servlet:
context-path: /minio
minio:
endpoint: http://ip:9002
domain: ${minio.endpoint}/${minio.bucketName}
bucketName: zjk-minio
accessKey: root
secretKey: password
secure: false
spring:
servlet:
multipart:
max-file-size: 100MB
max-request-size: 150MB
6、MinioConfig
package com.zjk.config;
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;
@Data
@Configuration
@ConfigurationProperties(prefix = "minio")
public class MinioConfig {
private String endpoint;
private String domain;
private int port;
private String accessKey;
private String secretKey;
private String bucketName;
@Bean
public MinioClient getMinioClient(){
MinioClient minioClient = MinioClient.builder().endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
return minioClient;
}
}
7、实现类
package com.zjk.common.utils.file;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import com.zjk.common.config.MinioConfig;
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.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
@Slf4j
@Component
public class MinioUtils {
/**
* 默认大小 500M
*/
public static final long DEFAULT_MAX_SIZE = 512 * 1024 * 1024;
private static final int DEFAULT_EXPIRY_TIME = 7 * 24 * 3600;
/**
* 默认的文件名最大长度 100
*/
public static final int DEFAULT_FILE_NAME_LENGTH = 100;
@Autowired
private MinioConfig minioConfig;
@Autowired
private MinioClient minioClient;
/**
* 本地文件上传接口
*
* @param bucketName
* @param file 上传的文件
* @return 访问地址
*/
public String uploadFile(String bucketName, MultipartFile file) throws Exception {
String fileName = file.getOriginalFilename();
PutObjectArgs args = PutObjectArgs.builder()
.bucket(bucketName)
.object(fileName)
.stream(file.getInputStream(), file.getInputStream().available(), -1)
//.contentType(file.getContentType())
//不这样写超过5M会报错
.contentType("application/octet-strem")
.build();
minioClient.putObject(args);
return minioConfig.getDomain() + "/" + fileName;
}
/**
* 本地文件上传接口(上传到指定的目录)
*
* @param bucketName
* @param file 上传的文件
* @return 访问地址
*/
public String uploadFileWithPrefix(String bucketName, String directory, MultipartFile file) throws Exception {
String fileName = file.getOriginalFilename();
InputStream inputStream = file.getInputStream();
PutObjectArgs args = PutObjectArgs.builder()
.bucket(bucketName)
.object(directory + "/" + fileName)
.stream(inputStream, inputStream.available(), -1)
.contentType("application/octet-strem")
.build();
minioClient.putObject(args);
return minioConfig.getDomain() + "/" + directory + "/" + fileName;
}
public List<String> modulesMultiUpload(String bucketName, MultipartFile[] files) throws Exception {
List<String> urlList = new ArrayList<>(files.length);
for (MultipartFile file : files) {
String url = uploadFile(bucketName, file);
urlList.add(url);
}
return urlList;
}
public void download(String bucketName, String originName, String fileName, HttpServletResponse response) {
GetObjectArgs build = GetObjectArgs
.builder()
.bucket(bucketName)
.object(fileName.replaceFirst("/" + bucketName + "/", ""))
.build();
try (InputStream inputStream = minioClient.getObject(build); OutputStream outputStream = response.getOutputStream()) {
String outputName = Optional
.ofNullable(originName)
.filter(StrUtil::isNotBlank)
.orElse("项目文件." + fileName.substring(fileName.lastIndexOf(".") + 1));
// 获取文件对象
response.reset();
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(outputName, "UTF-8"));
// 输出文件
// todo zys: 流直接转换
IoUtil.copy(inputStream, outputStream);
} catch (Exception ex) {
response.setHeader("Content-type", "text/html;charset=UTF-8");
String data = "文件下载失败";
try {
OutputStream ps = response.getOutputStream();
ps.write(data.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
log.error("minio文件下载失败", e);
}
}
}
// public void multiDownload(String bucketName, List<Map<String, String>> mapList, HttpServletResponse response) {
//
// InputStream inputStream = null;
// OutputStream outputStream = null;
// try {
// for (int i = 0; i < mapList.size(); i++) {
// String originName = mapList.get(i).get("originName");
// String fileName = mapList.get(i).get("fileName");
// if(StringUtils.isEmpty(originName)){
// originName = URLEncoder.encode(fileName.substring(fileName.lastIndexOf("/") + 1), "UTF-8");
// }
// outputStream = response.getOutputStream();
// // 获取文件对象
// inputStream = minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(fileName.replaceFirst("/" + bucketName + "/", "")).build());
// byte[] buf = new byte[1024];
// int length = 0;
// response.setHeader("Content-Disposition", "attachment;filename=" +
// URLEncoder.encode(fileName.substring(fileName.lastIndexOf("/") + 1), "UTF-8"));
// response.setContentType("application/octet-stream");
// response.setCharacterEncoding("UTF-8");
// // 输出文件
// while ((length = inputStream.read(buf)) > 0) {
// outputStream.write(buf, 0, length);
// }
// }
//
// } catch (Throwable ex) {
// response.reset();
// response.setHeader("Content-type", "text/html;charset=UTF-8");
// String data = "文件下载失败";
// try {
// OutputStream ps = response.getOutputStream();
// ps.write(data.getBytes(StandardCharsets.UTF_8));
// } catch (IOException e) {
// log.error("minio文件下载失败", e);
// }
// } finally {
// try {
// if (outputStream != null) {
// outputStream.close();
// }
// if (inputStream != null) {
// inputStream.close();
// }
// } catch (IOException e) {
// log.error("minio文件流关闭失败", e);
// }
// }
// }
/**
* 检查存储桶是否存在
*
* @param bucketName 存储桶名称
* @return boolean
*/
public boolean bucketExists(String bucketName) throws ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
}
/**
* 创建存储桶
*
* @param bucketName 存储桶名称
*/
public boolean makeBucket(String bucketName) throws InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, RegionConflictException, ServerException {
boolean flag = bucketExists(bucketName);
if (!flag) {
minioClient.makeBucket(
MakeBucketArgs.builder()
.bucket(bucketName)
.build());
return true;
} else {
return false;
}
}
/**
* 列出所有存储桶名称
*
* @return List<String>
*/
public List<String> listBucketNames() throws io.minio.errors.ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
List<Bucket> bucketList = minioClient.listBuckets();
List<String> bucketListName = new ArrayList<>();
for (Bucket bucket : bucketList) {
bucketListName.add(bucket.name());
}
return bucketListName;
}
/**
* 删除存储桶
*
* @param bucketName 存储桶名称
* @return boolean
*/
public boolean removeBucket(String bucketName) throws InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, io.minio.errors.ServerException {
boolean flag = bucketExists(bucketName);
if (flag) {
Iterable<Result<Item>> myObjects = listObjects(bucketName);
for (Result<Item> result : myObjects) {
Item item = result.get();
// 有对象文件,则删除失败
if (item.size() > 0) {
return false;
}
}
// 删除存储桶,注意,只有存储桶为空时才能删除成功。
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
flag = bucketExists(bucketName);
if (!flag) {
return true;
}
}
return false;
}
/**
* 列出存储桶中的所有对象
*
* @param bucketName 存储桶名称
* @return Iterable<Result<Item>>
*/
public Iterable<Result<Item>> listObjects(String bucketName) throws XmlParserException, IOException, InvalidResponseException, InvalidKeyException, NoSuchAlgorithmException, ServerException, ErrorResponseException, InvalidBucketNameException, InsufficientDataException, InternalException {
boolean flag = bucketExists(bucketName);
if (flag) {
return minioClient.listObjects( ListObjectsArgs.builder().bucket(bucketName).build());
}
return null;
}
/**
* 删除一个对象
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
*/
public boolean removeObject(String bucketName, String objectName) throws InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, io.minio.errors.ServerException {
boolean flag = bucketExists(bucketName);
if (flag) {
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
return true;
}
return false;
}
/**
* 删除一个对象
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
*/
public boolean removeObjectWithPrefix(String bucketName, String prefix, String objectName) throws InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, io.minio.errors.ServerException {
boolean flag = bucketExists(bucketName);
if (flag) {
String objectPath = prefix + "/" + objectName;
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectPath).build());
return true;
}
return false;
}
/**
* 删除指定桶的多个文件对象,返回删除错误的对象列表,全部删除成功,返回空列表
*
* @param bucketName 存储桶名称
* @param objectNames 含有要删除的多个object名称的迭代器对象
* @return
* eg:
* List<DeleteObject> objects = new LinkedList<>();
* objects.add(new DeleteObject("my-objectname1"));
* objects.add(new DeleteObject("my-objectname2"));
* objects.add(new DeleteObject("my-objectname3"));
*/
public List<String> removeObjects(String bucketName, List<DeleteObject> objectNames) throws InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, io.minio.errors.ServerException {
List<String> deleteErrorNames = new ArrayList<>();
boolean flag = bucketExists(bucketName);
if (flag) {
Iterable<Result<DeleteError>> results = minioClient.removeObjects(RemoveObjectsArgs.builder().bucket(bucketName).objects(objectNames).build());
for (Result<DeleteError> result : results) {
DeleteError error = result.get();
deleteErrorNames.add(error.objectName());
}
}
return deleteErrorNames;
}
/**
* 生成一个给HTTP GET请求用的presigned URL。
* 浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。
*
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
* @param expires 失效时间(以秒为单位),默认是7天,不得大于七天
* @return
*/
public String getPresignedObjectUrl(String bucketName, String objectName, Integer expires) throws ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, InvalidExpiresRangeException {
boolean flag = bucketExists(bucketName);
String url = "";
if (flag) {
if (expires < 1 || expires > DEFAULT_EXPIRY_TIME) {
throw new InvalidExpiresRangeException(expires,
"expires must be in range of 1 to " + DEFAULT_EXPIRY_TIME);
}
try {
url = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
.method(Method.GET)
.bucket(bucketName)
.object(objectName)
.expiry(expires)//动态参数
// .expiry(24 * 60 * 60)//用秒来计算一天时间有效期
// .expiry(1, TimeUnit.DAYS)//按天传参
// .expiry(1, TimeUnit.HOURS)//按小时传参数
.build());
} catch (ErrorResponseException | InsufficientDataException | InternalException | InvalidBucketNameException | InvalidExpiresRangeException | InvalidKeyException | InvalidResponseException | IOException | NoSuchAlgorithmException | ServerException | XmlParserException e) {
log.info(e.getMessage());
}
}
return url;
}
/**
* 文件访问路径,这里指没有nginx代理,直接从minio自带9002端口访问的情况下
* 一般会使用nginx代理一遍,以保证和web服务的端口一致
* @param bucketName 存储桶名称
* @param objectName 存储桶里的对象名称
* @return String
*/
public String getObjectUrl(String bucketName, String objectName) throws InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException, ServerException {
boolean flag = bucketExists(bucketName);
String url = "";
if (flag) {
try {
url = minioClient.getObjectUrl(bucketName, objectName);
} catch (ErrorResponseException e) {
log.error("XmlParserException",e);
} catch (InsufficientDataException e) {
log.error("InsufficientDataException",e);
} catch (InternalException e) {
log.error("InternalException",e);
} catch (InvalidBucketNameException e) {
log.error("InvalidBucketNameException",e);
} catch (InvalidKeyException e) {
log.error("InvalidKeyException",e);
} catch (InvalidResponseException e) {
log.error("InvalidResponseException",e);
} catch (IOException e) {
log.error("IOException",e);
} catch (NoSuchAlgorithmException e) {
log.error("NoSuchAlgorithmException",e);
} catch (XmlParserException e) {
log.error("XmlParserException",e);
} catch (io.minio.errors.ServerException e) {
throw new RuntimeException(e);
}
}
return url;
}
public byte[] getObject(String bucketName, String objectName) throws ServerException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
try (InputStream stream = minioClient.getObject(
GetObjectArgs.builder()
.bucket(bucketName)
.object(objectName)
.build());
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) {
byte[] buffer = new byte[1024];
int length;
while ((length = stream.read(buffer)) != -1) {
byteArrayOutputStream.write(buffer, 0, length);
}
return byteArrayOutputStream.toByteArray();
}
}
public File getFileFromMinioAsStream(String bucketName, String fileName, String originName) throws IOException {
GetObjectArgs build = GetObjectArgs
.builder()
.bucket(bucketName)
.object(fileName.replaceFirst("/" + bucketName + "/", ""))
.build();
try (InputStream inputStream = minioClient.getObject(build)) {
// 创建一个临时文件
int lastIndex = originName.lastIndexOf(".");
// 截取最后一个分隔符前的部分
String beforeLastDelimiter = originName.substring(0, lastIndex);
// 截取最后一个分隔符后的部分
String afterLastDelimiter = originName.substring(lastIndex + ".".length());
Path tempFilePath = Files.createTempFile(beforeLastDelimiter, "." + afterLastDelimiter);
File tempFile = tempFilePath.toFile();
// 将输入流写入临时文件
try (FileOutputStream outputStream = new FileOutputStream(tempFile)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, bytesRead);
}
}
// 返回临时文件的File对象(注意:这里返回的File对象指向的是临时文件,你可能需要在不再需要时删除它)
return tempFile;
} catch (MinioException e) {
// 处理MinIO相关的异常
e.printStackTrace();
throw new IOException("Failed to download file from MinIO", e);
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
} catch (InvalidKeyException e) {
throw new RuntimeException(e);
}
}
}

浙公网安备 33010602011771号