package com.sandu.ximon.admin.minio.utils;
|
|
import com.alibaba.fastjson.JSONObject;
|
import com.sandu.ximon.admin.config.VnnoxConstant;
|
import com.sandu.ximon.admin.minio.config.MinIoPolicy;
|
import com.sandu.ximon.admin.minio.entity.MinIoConstant;
|
import com.sandu.ximon.admin.utils.LogUtils;
|
import com.sandu.ximon.admin.utils.SpringContextUtils;
|
|
|
import io.minio.*;
|
import io.minio.errors.*;
|
import io.minio.messages.Bucket;
|
import io.minio.messages.DeleteError;
|
import io.minio.messages.Item;
|
import jdk.nashorn.internal.runtime.regexp.joni.exception.InternalException;
|
import lombok.SneakyThrows;
|
import org.apache.tomcat.util.http.fileupload.IOUtils;
|
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.IOException;
|
import java.io.InputStream;
|
import java.net.URLEncoder;
|
import java.security.InvalidKeyException;
|
import java.security.NoSuchAlgorithmException;
|
import java.util.ArrayList;
|
import java.util.Iterator;
|
import java.util.List;
|
|
|
/**
|
* 调用本工具类请保证bucketName不为空,不可在根目录添加文件
|
*/
|
@Component("MinIoUtil")
|
public class MinIoUtil {
|
|
@Autowired
|
private MinioClient minioClient;
|
|
// URL过期时间
|
private static final Integer DEFAULT_EXPIRY_TIME = 60 * 60 * 24;
|
|
|
public static MinIoUtil getMinIOUtil() {
|
return (MinIoUtil) SpringContextUtils.getBean("MinIoUtil");
|
}
|
|
|
/**
|
* 获取存储桶策略
|
*
|
* @param bucketName 存储桶名称
|
* @return json
|
*/
|
private JSONObject getBucketPolicy(String bucketName)
|
throws IOException, InvalidKeyException, InvalidResponseException, BucketPolicyTooLargeException, NoSuchAlgorithmException, ServerException, InternalException, XmlParserException, InvalidBucketNameException, InsufficientDataException, ErrorResponseException, io.minio.errors.InternalException {
|
String bucketPolicy = minioClient
|
.getBucketPolicy(GetBucketPolicyArgs.builder().bucket(bucketName).build());
|
return JSONObject.parseObject(bucketPolicy);
|
}
|
|
/**
|
* 设置开放*的桶策略
|
*
|
* @param bucketName
|
* @return
|
*/
|
public void setBucketPolicy(String bucketName) throws Exception {
|
minioClient.setBucketPolicy(
|
SetBucketPolicyArgs.builder().bucket(bucketName).config(MinIoPolicy.generateMinioPolicy(bucketName)).build()
|
);
|
}
|
|
|
/**
|
* 判断 bucket是否存在
|
*
|
* @param bucketName: 桶名
|
* @return: boolean
|
*/
|
@SneakyThrows(Exception.class)
|
public boolean bucketExists(String bucketName) {
|
return minioClient.bucketExists(bucketName);
|
}
|
|
public boolean objectExists(String bucketName, String objectName) {
|
try {
|
getObjectInfo(bucketName, objectName);
|
} catch (Exception e) {
|
return false;
|
}
|
return true;
|
}
|
|
/**
|
* 创建 bucket
|
*
|
* @param bucketName: 桶名
|
* @return: void
|
*/
|
@SneakyThrows(Exception.class)
|
public void createBucket(String bucketName) {
|
boolean isExist = minioClient.bucketExists((BucketExistsArgs) ((io.minio.BucketExistsArgs.Builder) BucketExistsArgs.builder().bucket(bucketName)).build());
|
if (!isExist) {
|
minioClient.makeBucket((MakeBucketArgs) ((io.minio.MakeBucketArgs.Builder) MakeBucketArgs.builder().bucket(bucketName)).build());
|
}
|
}
|
|
/**
|
* 获取全部bucket
|
*/
|
@SneakyThrows(Exception.class)
|
public List<Bucket> getAllBuckets() {
|
return minioClient.listBuckets();
|
}
|
|
/**
|
* 列出存储桶中的所有对象名称
|
*
|
* @param bucketName 存储桶名称
|
* @return
|
*/
|
@SneakyThrows
|
public List<String> listObjectNames(String bucketName) {
|
List<String> listObjectNames = new ArrayList<>();
|
boolean flag = bucketExists(bucketName);
|
if (flag) {
|
Iterable<Result<Item>> myObjects = listObjects(bucketName);
|
for (Result<Item> result : myObjects) {
|
Item item = result.get();
|
listObjectNames.add(item.objectName());
|
}
|
}
|
return listObjectNames;
|
}
|
|
/**
|
* 列出存储桶中的所有对象
|
*
|
* @param bucketName 存储桶名称
|
* @return
|
*/
|
@SneakyThrows
|
public Iterable<Result<Item>> listObjects(String bucketName) {
|
boolean flag = bucketExists(bucketName);
|
if (flag) {
|
return minioClient.listObjects(bucketName);
|
}
|
return null;
|
}
|
|
|
/**
|
* 文件上传
|
*
|
* @param bucketName: 桶名
|
* @param fileName: 文件名
|
* @param filePath: 文件路径
|
* @return: void
|
*/
|
@SneakyThrows(Exception.class)
|
public void uploadToMinIo(String bucketName, String fileName, String filePath) {
|
if (!bucketExists(bucketName)) {
|
createBucket(bucketName);
|
|
LogUtils.print("创建桶:" + bucketName);
|
|
}
|
// log.log(new LogRecordEntity(
|
// "服务器文件上传:(bucket)"+bucketName +", (fileName)"+fileName+",(filePath)"+filePath,
|
// LogType.MINIO_SERVER,
|
// getNUserId(),
|
// this.getClass()
|
// ));
|
try {
|
// 判断文件是否存在
|
if (!objectExists(bucketName, fileName)) {
|
minioClient.putObject(bucketName, fileName, filePath, null);
|
} else {
|
// log.log(new LogRecordEntity(
|
// "服务器文件已存在,请勿重复上传:(bucket)"+bucketName +", (fileName)"+fileName,
|
// LogType.MINIO_SERVER,
|
// getNUserId(),
|
// this.getClass()
|
// ));
|
}
|
} catch (Exception e) {
|
e.printStackTrace();
|
}
|
}
|
|
/**
|
* 文件上传
|
*
|
* @param bucketName: 桶名
|
* @param fileName: 文件名
|
* @param stream: 文件流
|
* @return: 文件url地址
|
*/
|
public String upload(String bucketName, String fileName, InputStream stream) {
|
if (!bucketExists(bucketName)) {
|
createBucket(bucketName);
|
|
LogUtils.print("创建桶:" + bucketName);
|
|
}
|
|
try {
|
// 判断文件是否存在
|
if (!objectExists(bucketName, fileName)) {
|
minioClient.putObject(bucketName, fileName, stream, new PutObjectOptions(stream.available(), -1));
|
} else {
|
|
return null;
|
}
|
} catch (Exception e) {
|
return null;
|
}
|
return getFileUrl(bucketName, fileName);
|
}
|
|
/**
|
* 列出所有存储桶名称
|
*
|
* @return
|
*/
|
@SneakyThrows
|
public List<String> listBucketNames() {
|
List<Bucket> bucketList = getAllBuckets();
|
List<String> bucketListName = new ArrayList<>();
|
for (Bucket bucket : bucketList) {
|
bucketListName.add(bucket.name());
|
}
|
return bucketListName;
|
}
|
|
|
/**
|
* 文件上传
|
*
|
* @param bucketName: 桶名
|
* @param file: 文件
|
* @return: 文件url地址
|
*/
|
@SneakyThrows(Exception.class)
|
public String upload(String bucketName, MultipartFile file) {
|
|
if (!bucketExists(bucketName)) {
|
createBucket(bucketName);
|
LogUtils.print("创建桶:" + bucketName);
|
if (bucketName.indexOf(VnnoxConstant.MINIO_PREFIX) != -1) {
|
setBucketPolicy(bucketName);
|
}
|
}
|
|
final InputStream is = file.getInputStream();
|
final String fileName = file.getOriginalFilename();
|
Long start = System.currentTimeMillis();
|
minioClient.putObject(bucketName, fileName, is, new PutObjectOptions(is.available(), -1));
|
is.close();
|
LogUtils.error("上传总时间:" + (System.currentTimeMillis() - start) + "ms");
|
return getFileUrl(bucketName, fileName);
|
}
|
|
/**
|
* 删除文件
|
*
|
* @param bucketName: 桶名
|
* @param fileName: 文件名
|
* @return: void
|
* @date : 2020/8/16 20:53
|
*/
|
@SneakyThrows(Exception.class)
|
public boolean deleteFile(String bucketName, String fileName) {
|
// 判断文件是否存在
|
if (objectExists(bucketName, fileName)) {
|
// log.log(new LogRecordEntity(
|
// "服务器文件删除:(bucket)"+bucketName +", (fileName)"+fileName,
|
// LogType.MINIO_SERVER,
|
// getNUserId(),
|
// this.getClass()
|
// ));
|
minioClient.removeObject(bucketName, fileName);
|
if (!objectExists(bucketName, fileName)) {
|
return true;
|
} else {
|
return false;
|
}
|
} else {
|
// log.log(new LogRecordEntity(
|
// "服务器文件不存在,无法删除:(bucket)"+bucketName +", (fileName)"+fileName,
|
// LogType.MINIO_SERVER,
|
// getNUserId(),
|
// this.getClass()
|
// ));
|
return false;
|
}
|
|
}
|
|
/**
|
* 删除指定桶的多个文件对象,返回删除错误的对象列表,全部删除成功,返回空列表
|
*
|
* @param bucketName 存储桶名称
|
* @param objectNames 含有要删除的多个object名称的迭代器对象
|
* @return
|
*/
|
@SneakyThrows
|
public List<String> removeObject(String bucketName, List<String> objectNames) {
|
List<String> deleteErrorNames = new ArrayList<>();
|
boolean flag = bucketExists(bucketName);
|
if (flag) {
|
Iterable<Result<DeleteError>> results = minioClient.removeObjects(bucketName, objectNames);
|
for (Result<DeleteError> result : results) {
|
DeleteError error = result.get();
|
deleteErrorNames.add(error.objectName());
|
}
|
}
|
// log.log(new LogRecordEntity(
|
// "服务器文件批量删除:(bucket)"+bucketName +", (fileName)"+ JSON.toJSONString(objectNames)+", 删除失败文件:"+JSON.toJSONString(deleteErrorNames),
|
// LogType.MINIO_SERVER,
|
// getNUserId(),
|
// this.getClass()
|
// ));
|
return deleteErrorNames;
|
}
|
|
|
/**
|
* 下载文件
|
*
|
* @param bucketName: 桶名
|
* @param fileName: 文件名
|
* @param response:
|
* @return: void
|
* @date : 2020/8/17 0:34
|
*/
|
@SneakyThrows(Exception.class)
|
public HttpServletResponse download(String bucketName, String fileName, HttpServletResponse response) {
|
// 获取对象的元数据
|
final ObjectStat stat = minioClient.statObject(bucketName, fileName);
|
response.setContentType(stat.contentType());
|
response.setCharacterEncoding("UTF-8");
|
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
|
InputStream is = minioClient.getObject(bucketName, fileName);
|
IOUtils.copy(is, response.getOutputStream());
|
is.close();
|
return response;
|
}
|
|
/**
|
* 获取MinIo文件的永久下载地址
|
*
|
* @param bucketName: 桶名
|
* @param fileName: 文件名
|
* @return:
|
*/
|
@SneakyThrows(Exception.class)
|
public String getFileUrl(String bucketName, String fileName) {
|
// log.log(new LogRecordEntity(
|
// "服务器文件获取永久下载地址:(bucketName)"+bucketName+", (fileName)"+fileName,
|
// LogType.MINIO_SERVER,
|
// getNUserId(),
|
// this.getClass()
|
// ));
|
return minioClient.presignedGetObject(bucketName, fileName);
|
}
|
|
|
/**
|
* 生成一个给HTTP GET请求用的presigned URL。
|
* 浏览器/移动端的客户端可以用这个URL进行下载,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。
|
*
|
* @param bucketName 存储桶名称
|
* @param objectName 存储桶里的对象名称
|
* @param expires 失效时间(以秒为单位
|
* @return
|
*/
|
@SneakyThrows
|
public String presignedGetObject(String bucketName, String objectName, Integer expires) {
|
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);
|
}
|
url = minioClient.presignedGetObject(bucketName, objectName, expires);
|
}
|
// log.log(new LogRecordEntity(
|
// "服务器文件获取文件定时下载地址(get:"+expires+"s):(bucketName)"+bucketName+", (fileName)"+objectName,
|
// LogType.MINIO_SERVER,
|
// getNUserId(),
|
// this.getClass()
|
// ));
|
return url;
|
}
|
|
/**
|
* 生成一个给HTTP PUT请求用的presigned URL。
|
* 浏览器/移动端的客户端可以用这个URL进行上传,即使其所在的存储桶是私有的。这个presigned URL可以设置一个失效时间,默认值是7天。
|
*
|
* @param bucketName 存储桶名称
|
* @param objectName 存储桶里的对象名称
|
* @param expires 失效时间(以秒为单位
|
* @return
|
*/
|
@SneakyThrows
|
public String presignedPutObject(String bucketName, String objectName, Integer expires) {
|
boolean flag = bucketExists(bucketName);
|
String url = "";
|
if (flag) {
|
if (expires < 10 || expires > DEFAULT_EXPIRY_TIME) {
|
throw new InvalidExpiresRangeException(expires,
|
"expires must be in range of 1 to " + DEFAULT_EXPIRY_TIME);
|
}
|
url = minioClient.presignedPutObject(bucketName, objectName, expires);
|
}
|
// log.log(new LogRecordEntity(
|
// "服务器文件获取文件定时下载地址(put:"+expires+"s):(bucketName)"+bucketName+", (fileName)"+objectName,
|
// LogType.MINIO_SERVER,
|
// getNUserId(),
|
// this.getClass()
|
// ));
|
return url;
|
}
|
|
/**
|
* 根据文件前置查询文件
|
*
|
* @param bucketName bucket名称
|
* @param prefix 前缀
|
* @param recursive 是否递归查询
|
* @return MinioItem 列表
|
*/
|
@SneakyThrows
|
public List getAllObjectsByPrefix(String bucketName, String prefix, boolean recursive) {
|
List<Item> list = new ArrayList<>();
|
Iterable<Result<Item>> objectsIterator = minioClient.listObjects(bucketName, prefix, recursive);
|
if (objectsIterator != null) {
|
Iterator<Result<Item>> iterator = objectsIterator.iterator();
|
if (iterator != null) {
|
while (iterator.hasNext()) {
|
Result<Item> result = iterator.next();
|
Item item = result.get();
|
list.add(item);
|
}
|
}
|
}
|
|
return list;
|
}
|
|
/**
|
* 获取文件
|
*
|
* @param bucketName bucket名称
|
* @param objectName 文件名称
|
* @return 二进制流
|
*/
|
@SneakyThrows
|
public InputStream getObject(String bucketName, String objectName) {
|
return minioClient.getObject(bucketName, objectName);
|
}
|
|
|
/**
|
* 获取文件信息, 如果抛出异常则说明文件不存在
|
*
|
* @param bucketName bucket名称
|
* @param objectName 文件名称
|
* @throws Exception https://docs.minio.io/cn/java-client-api-reference.html#statObject
|
*/
|
public ObjectStat getObjectInfo(String bucketName, String objectName) throws Exception {
|
ObjectStat objectStat = minioClient.statObject(bucketName, objectName);
|
return objectStat;
|
}
|
|
/**
|
* 获取文件绝对路径
|
*
|
* @param bucketName
|
* @param objectName
|
* @return
|
*/
|
public String getAbsolutePath(String bucketName, String objectName) {
|
try {
|
getObjectInfo(bucketName, objectName);
|
} catch (Exception e) {
|
return null;
|
}
|
return MinIoConstant.getAbsolutePath(bucketName, objectName);
|
}
|
|
}
|