赞
踩
package kl.gayxzc.csr.cilent;
import io.minio.GetObjectArgs;
import io.minio.GetObjectResponse;
import io.minio.GetPresignedObjectUrlArgs;
import io.minio.ListObjectsArgs;
import io.minio.MinioClient;
import io.minio.PutObjectArgs;
import io.minio.RemoveObjectArgs;
import io.minio.Result;
import io.minio.StatObjectArgs;
import io.minio.http.Method;
import io.minio.messages.Item;
import kl.gayxzc.csr.constant.CsrErrorCode;
import kl.gayxzc.csr.constant.MinioConstant;
import kl.gayxzc.csr.exception.MinioException;
import kl.gayxzc.csr.utils.DateUtils;
import kl.gayxzc.csr.utils.UUIDUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.FastByteArrayOutputStream;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* minio-client
* 文件管理客户端
*
* @author ldh
*/
@Slf4j
@Component("fileClient")
public class FileClient {
private MinioClient minioClient;
private BucketClient bucketClient;
@Value("${csr.minio.file-server-type}")
private String minioServerType;
@Autowired
public void setMinioClient(MinioClient minioClient) {
this.minioClient = minioClient;
}
@Autowired
public void setBucketClient(BucketClient bucketClient) {
this.bucketClient = bucketClient;
}
/**
* 上传文件到默认存储桶
*
* @param files 文件列表
* @return 上传后的路径
* @throws Exception 上传失败异常
*/
public List<String> put(MultipartFile... files) {
return put(null, files);
}
/**
* 上传文件到指定存储桶
*
* @param path 上传后的路径
* @param files 文件列表
* @return 上传后的路径
* @throws Exception 上传失败异常
*/
public List<String> put(String path, MultipartFile... files) {
if (files == null || files.length == 0) {
return new ArrayList<>();
}
List<String> list = new LinkedList<>();
for (MultipartFile file : files) {
list.add(put(path, file));
}
return list;
}
/**
* 下载默认存储桶中的文件
*
* @param fileName 文件名称
* @param response 响应体
* @throws Exception 下载失败异常
*/
public void download(String fileName, HttpServletResponse response) {
GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(bucketClient.getDefaultBucket())
.object(fileName).build();
try (GetObjectResponse getObjectRes = minioClient.getObject(objectArgs)) {
byte[] buf = new byte[1024];
int len;
try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
while ((len = getObjectRes.read(buf)) != -1) {
os.write(buf, 0, len);
}
os.flush();
byte[] bytes = os.toByteArray();
response.setCharacterEncoding("utf-8");
final String[] split = fileName.split(MinioConstant.DEFAULT_FILENAME_SEPARATOR);
String orgFilename = split.length > 1 ? split[split.length - 1] : fileName;
log.info("file original name is {}", orgFilename);
response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(orgFilename, "utf-8"));
try (ServletOutputStream stream = response.getOutputStream()) {
stream.write(bytes);
stream.flush();
}
}
} catch (Exception e) {
log.error("file {} download fail", fileName, e);
throw new MinioException(CsrErrorCode.DOWNLOAD_ERROR);
}
}
/**
* 下载指定存储桶中的文件
*
* @param fileName 文件名称
* @param name 指定桶名称
* @param response 响应体
* @throws Exception 下载失败异常
*/
public void download(String fileName, String name, HttpServletResponse response) {
GetObjectArgs objectArgs = GetObjectArgs.builder().bucket(name)
.object(fileName).build();
try (GetObjectResponse getObjectRes = minioClient.getObject(objectArgs)) {
byte[] buf = new byte[1024];
int len;
try (FastByteArrayOutputStream os = new FastByteArrayOutputStream()) {
while ((len = getObjectRes.read(buf)) != -1) {
os.write(buf, 0, len);
}
os.flush();
byte[] bytes = os.toByteArray();
response.setCharacterEncoding("utf-8");
final String[] split = fileName.split(MinioConstant.DEFAULT_FILENAME_SEPARATOR);
String orgFilename = split.length > 1 ? split[split.length - 1] : fileName;
log.info("file original name is {}", orgFilename);
response.addHeader("Content-Disposition", "attachment;fileName=" + URLEncoder.encode(orgFilename, "utf-8"));
try (ServletOutputStream stream = response.getOutputStream()) {
stream.write(bytes);
// 与框架冲突,保留框架层关闭 >> ResponseResult.success();
// stream.flush();
}
}
} catch (Exception e) {
log.error("file {} download fail", fileName, e);
throw new MinioException(CsrErrorCode.DOWNLOAD_ERROR);
}
}
/**
* 预览图片
*
* @param fileName 文件名称
* @return 预览地址
*/
public String preview(String fileName) {
// 查看文件地址
final GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs.builder()
.bucket(bucketClient.getDefaultBucket())
.object(fileName)
.method(Method.GET)
.build();
try {
return minioClient.getPresignedObjectUrl(build);
} catch (Exception e) {
log.error("file {} preview failed", fileName, e);
throw new MinioException(CsrErrorCode.PREVIEW_ERROR);
}
}
/**
* 预览图片
*
* @param fileName 文件名称
* @param bucketName 桶名称
* @return 预览地址
*/
public String preview(String fileName, String bucketName) {
// 查看文件地址
final GetPresignedObjectUrlArgs build = GetPresignedObjectUrlArgs.builder()
.bucket((Strings.isBlank(bucketName)) ? bucketClient.getDefaultBucket() : bucketName)
.object(fileName)
.method(Method.GET)
.build();
try {
return minioClient.getPresignedObjectUrl(build);
} catch (Exception e) {
log.error("file {} preview failed", fileName, e);
throw new MinioException(CsrErrorCode.PREVIEW_ERROR);
}
}
/**
* 删除⽂件
*
* @param objectName ⽂件名称
* @throws Exception 删除文件异常
*/
public void removeObject(String objectName) {
removeObject(bucketClient.getDefaultBucket(), objectName);
}
/**
* 删除⽂件
*
* @param bucketName bucket名称
* @param objectName ⽂件名称
* @throws Exception 删除文件异常
*/
public void removeObject(String bucketName, String objectName) {
try {
minioClient.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build());
} catch (Exception e) {
log.error("file {} remove failed", objectName, e);
throw new MinioException(CsrErrorCode.REMOVE_ERROR);
}
}
/**
* 删除文件夹及文件
*
* @param bucketName bucket名称
* @since tarzan LIU
*/
public void deleteObject(String bucketName) {
boolean flag = bucketClient.bucketExists(bucketName);
if (flag) {
try {
// 递归列举某个bucket下的所有文件,然后循环删除
log.info("递归列举{}下的所有文件循环删除", bucketName);
Iterable<Result<Item>> iterable = minioClient.listObjects(ListObjectsArgs.builder()
.bucket(bucketName)
.recursive(true)
.build());
for (Result<Item> itemResult : iterable) {
removeObject(bucketName, itemResult.get().objectName());
}
} catch (Exception e) {
log.error("递归列举{}下的所有文件循环删除异常", bucketName, e);
}
}
}
/**
* 上传单个文件
*
* @param file 文件
* @return 文件路径
* @throws Exception 上传失败异常
*/
public String put(MultipartFile file) {
return put(null, file);
}
/**
* 上传单个文件至默认桶
*
* @param path 指定路径
* @param file 文件
* @return 文件最终路径
* @throws Exception 文件上传失败异常
*/
public String put(String path, MultipartFile file) {
beforePutCheck(file);
String originalFilename = file.getOriginalFilename();
assert originalFilename != null;
String objectName = buildObjectName(originalFilename, path);
try {
minioClient.putObject(buildPutObjectArgs(objectName, file));
return objectName;
} catch (Exception e) {
log.error("file[{}] upload failed", originalFilename, e);
throw new MinioException(CsrErrorCode.FILE_UPLOAD_FAILED_ERROR);
}
}
/**
* 上传单个文件至指定桶
*
* @param path 指定路径
* @param file 文件
* @param bucketName 桶名称
* @return 文件最终路径
* @throws Exception 文件上传失败异常
*/
public String put(String path, MultipartFile file, String bucketName) {
beforePutCheck(file);
String originalFilename = file.getOriginalFilename();
assert originalFilename != null;
// String objectName = buildObjectName(originalFilename, path);
try {
minioClient.putObject(buildPutObjectArgs(path, file, bucketName));
return path;
} catch (Exception e) {
log.error("file[{}] upload failed", originalFilename, e);
throw new MinioException(CsrErrorCode.FILE_UPLOAD_FAILED_ERROR);
}
}
/**
* put 前校验
*
* @param file 文件
* @throws Exception put异常
*/
void beforePutCheck(MultipartFile file) {
String originalFilename = file.getOriginalFilename();
if (!StringUtils.hasText(originalFilename)) {
log.error("file {} cannot have an empty name", originalFilename);
throw new MinioException(CsrErrorCode.FILE_NAME_NO_NULL_ERROR);
}
}
/**
* 获取文件分隔符
*
* @return 文件分隔符
*/
String getFileSeparator() {
if (minioServerType.equals("WIN")) {
return MinioConstant.FILE_SEPARATOR_WIN;
} else if (minioServerType.equals("UNIX")) {
return MinioConstant.FILE_SEPARATOR_UNIX;
}
return File.separator;
}
/**
* 构建ObjectName
*
* @param originalFilename 文件原始名称
* @param path 文件路径
* @return ObjectName
*/
String buildObjectName(String originalFilename, String path) {
String filename = UUIDUtils.uuid() + MinioConstant.DEFAULT_FILENAME_SEPARATOR + originalFilename;
String objectName = DateUtils.getNowDateStr(DateUtils.PATTERN_YYYYMMDD) + getFileSeparator() + filename;
if (path != null) {
// TODO 手写路径换 File.separator
objectName = path + "/" + filename;
}
return objectName;
}
/**
* 构建 PutObjectArgs
*
* @param objectName put对象名称
* @param file 文件对象
* @return PutObjectArgs
* @throws Exception 输入输出流异常
*/
PutObjectArgs buildPutObjectArgs(String objectName, MultipartFile file) throws Exception {
return PutObjectArgs.builder()
.bucket(bucketClient.getDefaultBucket())
.object(objectName)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build();
}
/**
* 构建 PutObjectArgs
*
* @param objectName put对象名称
* @param file 文件对象
* @param bucketName 连接桶名称
* @return PutObjectArgs
* @throws Exception 输入输出流异常
*/
PutObjectArgs buildPutObjectArgs(String objectName, MultipartFile file, String bucketName) throws Exception {
return PutObjectArgs.builder()
.bucket((Strings.isBlank(bucketName)) ? bucketClient.getDefaultBucket() : bucketName)
.object(objectName)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build();
}
/**
* 判断默认存储桶文件是否存在
*
* @param objectName 文件名称, 如果要带文件夹请用 / 分割, 例如 /help/index.html
* @return true存在, 反之
*/
public Boolean checkFileIsExist(String objectName) {
return this.checkFileIsExist(bucketClient.getDefaultBucket(), objectName);
}
/**
* 判断默认存储桶文件夹是否存在
*
* @param folderName 文件夹名称
* @return true存在, 反之
*/
public Boolean checkFolderIsExist(String folderName) {
return this.checkFolderIsExist(bucketClient.getDefaultBucket(), folderName);
}
/**
* 判断文件是否存在
*
* @param bucketName 桶名称
* @param objectName 文件名称, 如果要带文件夹请用 / 分割, 例如 /help/index.html
* @return true存在, 反之
*/
public Boolean checkFileIsExist(String bucketName, String objectName) {
try {
minioClient.statObject(
StatObjectArgs.builder().bucket(bucketName).object(objectName).build()
);
} catch (Exception e) {
return false;
}
return true;
}
/**
* 判断文件夹是否存在
*
* @param bucketName 桶名称
* @param folderName 文件夹名称
* @return true存在, 反之
*/
public Boolean checkFolderIsExist(String bucketName, String folderName) {
try {
Iterable<Result<Item>> results = minioClient.listObjects(
ListObjectsArgs
.builder()
.bucket(bucketName)
.prefix(folderName)
.recursive(false)
.build());
for (Result<Item> result : results) {
Item item = result.get();
if (item.isDir() && folderName.equals(item.objectName())) {
return true;
}
}
} catch (Exception e) {
return false;
}
return true;
}
}
package kl.gayxzc.csr.cilent;
import io.minio.BucketExistsArgs;
import io.minio.MakeBucketArgs;
import io.minio.MinioClient;
import io.minio.RemoveBucketArgs;
import io.minio.SetBucketPolicyArgs;
import io.minio.messages.Bucket;
import kl.gayxzc.csr.constant.CsrErrorCode;
import kl.gayxzc.csr.constant.MinioConstant;
import kl.gayxzc.csr.exception.MinioException;
import lombok.extern.slf4j.Slf4j;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.util.List;
/**
* minio-client
* 存储桶管理客户端
*
* @author ldh
*/
@Slf4j
@Component("bucketClient")
public class BucketClient {
private MinioClient minioClient;
private String defaultBucket;
@Value("${csr.minio.bucket-name}")
private String minioName;
@Autowired
public void setMinioClient(MinioClient minioClient) {
this.minioClient = minioClient;
}
/**
* 校验桶是否存在
*
* @param bucketName 桶名称
* @return
*/
public Boolean bucketExists(String bucketName) {
try {
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
log.error("bucket {} cannot found", bucketName, e);
throw new MinioException(CsrErrorCode.CHECK_ERROR);
}
}
/**
* 获取默认存储桶
*
* @return 默认存储桶
* @throws Exception 获取失败
*/
public String getDefaultBucket() {
if (!StringUtils.hasText(defaultBucket)) {
defaultBucket = minioName;
if (!StringUtils.hasText(defaultBucket)) {
log.error("default bucket cannot be empty");
throw new MinioException(CsrErrorCode.MINIO_DEFAULT_ERROR);
}
}
return defaultBucket;
}
/**
* 设置默认存储桶
*
* @param name 存储桶名称
* @throws Exception 设置失败异常
*/
public void setDefaultBucket(String name) {
if (bucketExists(name)) {
this.defaultBucket = name;
return;
}
this.defaultBucket = minioName;
createBucket(this.defaultBucket);
if (!StringUtils.hasText(defaultBucket)) {
throw new MinioException(CsrErrorCode.MINIO_DEFAULT_ERROR);
}
}
/**
* 创建存储桶
*
* @param bucketName 存储桶名称
* @throws Exception 存储桶异常
*/
public void createBucket(String bucketName) {
if (!bucketExists(bucketName)) {
try {
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
log.error("存储桶异常", e);
throw new MinioException(CsrErrorCode.BUCKET_ERROR);
}
}
log.warn("bucket {} has created", bucketName);
}
/**
* 创建存储桶并赋予策略
*
* @param bucketName 存储桶名称
* @param policy 权限
* @throws Exception 存储桶异常
*/
public void createBucket(String bucketName, String policy) {
if (!bucketExists(bucketName)) {
try {
// 创建桶
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
if (Strings.isBlank(policy)) {
policy = MinioConstant.READ_WRITE;
}
if (Strings.isNotBlank(policy)) {
String strategy = this.consolidationStrategy(bucketName, policy);
minioClient.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucketName).config(strategy).build());
}
} catch (Exception e) {
log.error("存储桶异常", e);
throw new MinioException(CsrErrorCode.BUCKET_ERROR);
}
}
log.warn("bucket {} has created", bucketName);
}
/**
* 拼接Minio对应桶策略
*
* @param policy 权限code
* @return json 策略结构体
*/
private String consolidationStrategy(String bucketName, String policy) {
String jsonStrategy = Strings.EMPTY;
if (MinioConstant.READ_ONLY.equals(policy)) {
jsonStrategy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\"],\"Resource\":[\"arn:aws:s3:::" + bucketName + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetObject\"],\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"]}]}";
} else if (MinioConstant.WRITE_ONLY.equals(policy)) {
jsonStrategy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + bucketName + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:AbortMultipartUpload\",\"s3:DeleteObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\"],\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"]}]}";
} else if (MinioConstant.READ_WRITE.equals(policy)) {
jsonStrategy = "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetBucketLocation\",\"s3:ListBucket\",\"s3:ListBucketMultipartUploads\"],\"Resource\":[\"arn:aws:s3:::" + bucketName + "\"]},{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:DeleteObject\",\"s3:GetObject\",\"s3:ListMultipartUploadParts\",\"s3:PutObject\",\"s3:AbortMultipartUpload\"],\"Resource\":[\"arn:aws:s3:::" + bucketName + "/*\"]}]}";
} else {
try {
this.removeBucket(bucketName);
log.info("桶策略枚举值不合法,桶{}已删除", bucketName);
throw new MinioException(CsrErrorCode.POLICY_TYPE_ENUM_NOT_NULL);
} catch (Exception e) {
log.error("桶策略枚举值不合法", e);
}
}
return jsonStrategy;
}
/**
* 获取全部bucket
*
* @return 全部bucket信息
* @throws Exception 获取全部bucket信息失败异常
*/
public List<Bucket> getAllBuckets() {
try {
return minioClient.listBuckets();
} catch (Exception e) {
log.error("获取全部bucket信息失败异常", e);
throw new MinioException(CsrErrorCode.FAILED_ALL_INFORMATION_ERROR);
}
}
/**
* 根据bucketName删除信息
*
* @param bucketName bucket名称
* @throws Exception 删除bucket信息失败异常
*/
public void removeBucket(String bucketName) {
try {
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
} catch (Exception e) {
log.error("删除bucket信息失败异常", e);
throw new MinioException(CsrErrorCode.REMOVE_BUCKET_ERROR);
}
}
}
package kl.gayxzc.csr.constant;
/**
* MINIO_常量定义
*/
public class MinioConstant {
/**
* 文件名称分隔符
*/
public static final String DEFAULT_FILENAME_SEPARATOR = "@";
/**
* win
*/
public static final String FILE_SEPARATOR_WIN = "\\";
/**
* unix
*/
public static final String FILE_SEPARATOR_UNIX = "/";
/**
* bucket权限-只读 READ_ONLY
*/
public static final String READ_ONLY = "READ_ONLY";
/**
* bucket权限-只写 WRITE_ONLY
*/
public static final String WRITE_ONLY = "WRITE_ONLY";
/**
* bucket权限-读写 READ_WRITE
*/
public static final String READ_WRITE = "READ_WRITE";
}
package kl.gayxzc.csr.utils;
import java.util.UUID;
/**
* UUID工具类
*
* @author ldh
*/
public class UUIDUtils {
/**
* 随机获取uuid对象
*
* @return uuid对象
*/
public static UUID randomUuid() {
return UUID.randomUUID();
}
/**
* 获取原生uuid字符串
*
* @return 原生uuid字符串
*/
public static String orgUuid() {
return randomUuid().toString();
}
/**
* 获取 uuid
*
* @return uuid字符串
*/
public static String uuid() {
return orgUuid().replace("-", "");
}
}
package kl.gayxzc.csr.utils;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
/**
* 日期工具类
*
* @author ldh
*/
public class DateUtils {
public static final String PATTERN_YYYYMMDD = "yyyyMMdd";
public static final DateTimeFormatter DTF_PATTERN_YYYYMMDD = DateTimeFormatter.ofPattern(PATTERN_YYYYMMDD);
public static final String PATTERN_YYYY_MM_DD = "yyyy-MM-dd";
public static final DateTimeFormatter DTF_PATTERN_YYYY_MM_DD = DateTimeFormatter.ofPattern(PATTERN_YYYY_MM_DD);
/**
* /**
* 获取当前日期
*
* @return 当前日期 <java.time.LocalDate>
*/
public static LocalDate now() {
return LocalDate.now();
}
/**
* 获取当前日期字符串
*
* @return 时间
*/
public static String getNowDateStr() {
return now().format(DTF_PATTERN_YYYY_MM_DD);
}
/**
* 获取当前日期字符串转Long
*
* @return 时间
*/
public static Long getNowDateNumber() {
return Long.valueOf(getNowDateStr());
}
/**
* 获取当前日期字符串
*
* @param pattern 格式化
* @return 时间
*/
public static String getNowDateStr(String pattern) {
return now().format(DateTimeFormatter.ofPattern(pattern));
}
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。