feat: add minio
This commit is contained in:
@@ -4,9 +4,7 @@ import com.schisandra.auth.application.dto.SchisandraCaptchaDTO;
|
||||
import com.schisandra.auth.common.entity.CaptchaResult;
|
||||
import com.schisandra.auth.common.redis.RedisUtil;
|
||||
import com.schisandra.auth.common.utils.AESUtils;
|
||||
import com.schisandra.auth.common.utils.MD5Util;
|
||||
import com.schisandra.auth.common.utils.RotateImageUtils;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
@@ -17,7 +15,6 @@ import javax.annotation.Resource;
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Random;
|
||||
|
@@ -16,6 +16,12 @@ spring:
|
||||
- Path=/auth/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
- id: system
|
||||
uri: lb://schisandra-cloud-storage-system-dev
|
||||
predicates:
|
||||
- Path=/system/**
|
||||
filters:
|
||||
- StripPrefix=1
|
||||
# redis配置
|
||||
redis:
|
||||
# Redis数据库索引(默认为0)
|
||||
|
@@ -0,0 +1,47 @@
|
||||
package com.schisandra.oss.application.controller;
|
||||
|
||||
import com.schisandra.oss.application.oss.constant.OssConstant;
|
||||
import com.schisandra.oss.application.oss.utils.MinIOUtils;
|
||||
import com.schisandra.oss.domain.bo.SchisandraOssConfigBO;
|
||||
import com.schisandra.oss.domain.service.impl.SchisandraOssConfigDomainService;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.ObjectUtils;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
|
||||
/**
|
||||
* @Classname SchisandraMinioOssController
|
||||
* @BelongsProject: schisandra-cloud-storage
|
||||
* @BelongsPackage: com.schisandra.oss.application.controller
|
||||
* @Author: landaiqing
|
||||
* @CreateTime: 2024-05-13 00:04
|
||||
* @Description: TODO
|
||||
* @Version: 1.0
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/oss/minio/")
|
||||
@Slf4j
|
||||
public class SchisandraMinioOssController {
|
||||
|
||||
@Resource
|
||||
private SchisandraOssConfigDomainService schisandraOssConfigDomainService;
|
||||
|
||||
@RequestMapping("init")
|
||||
@SneakyThrows
|
||||
public void init() {
|
||||
SchisandraOssConfigBO minio = schisandraOssConfigDomainService.getOssConfig(OssConstant.OssType.MINIO);
|
||||
if (ObjectUtils.isEmpty(minio)) {
|
||||
log.error("minio配置信息获取失败");
|
||||
}
|
||||
new MinIOUtils(minio.getEndpoint(), minio.getBucketName(), minio.getAccessKey(), minio.getSecretKey(), minio.getImgSize(), minio.getFileSize());
|
||||
}
|
||||
|
||||
@GetMapping("test")
|
||||
public void test(){
|
||||
System.out.println(MinIOUtils.getBasisUrl());
|
||||
}
|
||||
}
|
@@ -18,5 +18,6 @@ public interface SchisandraOssConfigDTOConverter {
|
||||
SchisandraOssConfigDTOConverter INSTANCE = Mappers.getMapper(SchisandraOssConfigDTOConverter.class);
|
||||
|
||||
SchisandraOssConfigBO convertDTOToBO(SchisandraOssConfigDTO schisandraOssConfigDTO);
|
||||
SchisandraOssConfigDTO convertBOToDTO(SchisandraOssConfigBO schisandraOssConfig);
|
||||
|
||||
}
|
||||
|
@@ -0,0 +1,60 @@
|
||||
package com.schisandra.oss.application.oss.constant;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author landaiqing
|
||||
*/
|
||||
public class OssConstant {
|
||||
|
||||
public static final String OSS = "oss";
|
||||
public static final String ENABLE = "enable";
|
||||
public static final String DEFAULT_ENABLE_VALUE = "true";
|
||||
|
||||
public static final int KB = 1024;
|
||||
|
||||
public static final int MB = 1024 * KB;
|
||||
/**
|
||||
* 默认分片大小
|
||||
*/
|
||||
public static final Long DEFAULT_PART_SIZE = 5L * MB;
|
||||
/**
|
||||
* 默认缓冲区大小
|
||||
*/
|
||||
public static final int DEFAULT_BUFFER_SIZE = 8 * KB;
|
||||
/**
|
||||
* 默认最大分片数
|
||||
*/
|
||||
public static final Long DEFAULT_PART_NUM = 10000L;
|
||||
/**
|
||||
* 默认并发线程数
|
||||
*/
|
||||
public static final Integer DEFAULT_TASK_NUM = Runtime.getRuntime().availableProcessors();
|
||||
|
||||
public static final Long DEFAULT_CONNECTION_TIMEOUT = TimeUnit.MINUTES.toMillis(5);
|
||||
|
||||
/**
|
||||
* OSS存储类型
|
||||
*/
|
||||
public interface OssType {
|
||||
String LOCAL = "local";
|
||||
String FTP = "ftp";
|
||||
String SFTP = "sftp";
|
||||
String ALI = "ali";
|
||||
String QINIU = "qiniu";
|
||||
String MINIO = "minio";
|
||||
String BAIDU = "baidu";
|
||||
String TENCENT = "tencent";
|
||||
String HUAWEI = "huawei";
|
||||
String JD = "jd";
|
||||
String UP = "up";
|
||||
String JINSHAN = "jinshan";
|
||||
String WANGYI = "wangyi";
|
||||
String UCLOUD = "ucloud";
|
||||
String PINGAN = "pingan";
|
||||
String QINGYUN = "qingyun";
|
||||
String JDBC = "jdbc";
|
||||
String AWS = "aws";
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,107 @@
|
||||
package com.schisandra.oss.application.oss.constant;
|
||||
|
||||
/**
|
||||
* @author landaiqing
|
||||
*/
|
||||
public enum OssType {
|
||||
|
||||
/**
|
||||
* 本地磁盘存储
|
||||
*/
|
||||
LOCAL(OssConstant.OssType.LOCAL),
|
||||
|
||||
/**
|
||||
* FTP协议存储
|
||||
*/
|
||||
FTP(OssConstant.OssType.FTP),
|
||||
|
||||
/**
|
||||
* SFTP存储
|
||||
*/
|
||||
SFTP(OssConstant.OssType.SFTP),
|
||||
|
||||
/**
|
||||
* 阿里OSS存储
|
||||
*/
|
||||
ALI(OssConstant.OssType.ALI),
|
||||
|
||||
/**
|
||||
* 七牛云存储
|
||||
*/
|
||||
QINIU(OssConstant.OssType.QINIU),
|
||||
|
||||
/**
|
||||
* MinIO存储
|
||||
*/
|
||||
MINIO(OssConstant.OssType.MINIO),
|
||||
|
||||
/**
|
||||
* 百度云存储
|
||||
*/
|
||||
BAIDU(OssConstant.OssType.BAIDU),
|
||||
|
||||
/**
|
||||
* 腾讯云存储
|
||||
*/
|
||||
TENCENT(OssConstant.OssType.TENCENT),
|
||||
|
||||
/**
|
||||
* 华为云存储
|
||||
*/
|
||||
HUAWEI(OssConstant.OssType.HUAWEI),
|
||||
|
||||
/**
|
||||
* 京东云存储
|
||||
*/
|
||||
JD(OssConstant.OssType.JD),
|
||||
|
||||
/**
|
||||
* 又拍云存储
|
||||
*/
|
||||
UP(OssConstant.OssType.UP),
|
||||
|
||||
/**
|
||||
* 金山云
|
||||
*/
|
||||
JINSHAN(OssConstant.OssType.JINSHAN),
|
||||
|
||||
/**
|
||||
* 网易数帆
|
||||
*/
|
||||
WANGYI(OssConstant.OssType.WANGYI),
|
||||
|
||||
/**
|
||||
* UCloud
|
||||
*/
|
||||
UCLOUD(OssConstant.OssType.UCLOUD),
|
||||
|
||||
/**
|
||||
* 平安云
|
||||
*/
|
||||
PINGAN(OssConstant.OssType.PINGAN),
|
||||
|
||||
/**
|
||||
* 青云
|
||||
*/
|
||||
QINGYUN(OssConstant.OssType.QINGYUN),
|
||||
|
||||
/**
|
||||
* JDBC
|
||||
*/
|
||||
JDBC(OssConstant.OssType.JDBC),
|
||||
|
||||
/**
|
||||
* 亚马逊
|
||||
*/
|
||||
AWS(OssConstant.OssType.AWS);
|
||||
|
||||
private final String value;
|
||||
|
||||
OssType(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String getValue() {
|
||||
return value;
|
||||
}
|
||||
}
|
@@ -0,0 +1,52 @@
|
||||
package com.schisandra.oss.application.oss.enums;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
|
||||
public enum OssContentType {
|
||||
DEFAULT("default","application/octet-stream"),
|
||||
JPG("jpg", "image/jpeg"),
|
||||
TIFF("tiff", "image/tiff"),
|
||||
GIF("gif", "image/gif"),
|
||||
JFIF("jfif", "image/jpeg"),
|
||||
PNG("png", "image/png"),
|
||||
TIF("tif", "image/tiff"),
|
||||
ICO("ico", "image/x-icon"),
|
||||
JPEG("jpeg", "image/jpeg"),
|
||||
WBMP("wbmp", "image/vnd.wap.wbmp"),
|
||||
FAX("fax", "image/fax"),
|
||||
NET("net", "image/pnetvue"),
|
||||
JPE("jpe", "image/jpeg"),
|
||||
RP("rp", "image/vnd.rn-realpix"),
|
||||
MP4("mp4", "video/mp4");
|
||||
|
||||
private String prefix;
|
||||
|
||||
private String type;
|
||||
|
||||
public static String getContentType(String prefix){
|
||||
if(StrUtil.isEmpty(prefix)){
|
||||
return DEFAULT.getType();
|
||||
}
|
||||
prefix = prefix.substring(prefix.lastIndexOf(".") + 1);
|
||||
for (OssContentType value : OssContentType.values()) {
|
||||
if(prefix.equalsIgnoreCase(value.getPrefix())){
|
||||
return value.getType();
|
||||
}
|
||||
}
|
||||
return DEFAULT.getType();
|
||||
}
|
||||
|
||||
OssContentType(String prefix, String type) {
|
||||
this.prefix = prefix;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public String getPrefix() {
|
||||
return prefix;
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,439 @@
|
||||
package com.schisandra.oss.application.oss.utils;
|
||||
|
||||
|
||||
import io.minio.*;
|
||||
import io.minio.http.Method;
|
||||
import io.minio.messages.Bucket;
|
||||
import io.minio.messages.DeleteObject;
|
||||
import io.minio.messages.Item;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* @Classname MinIOUtils
|
||||
* @BelongsProject: schisandra-cloud-storage
|
||||
* @BelongsPackage: com.schisandra.oss.application.oss.utils
|
||||
* @Author: landaiqing
|
||||
* @CreateTime: 2024-05-12 23:50
|
||||
* @Description: MinIO工具类
|
||||
* @Version: 1.0
|
||||
*/
|
||||
@Slf4j
|
||||
public class MinIOUtils {
|
||||
|
||||
private static MinioClient minioClient;
|
||||
|
||||
private static String endpoint;
|
||||
private static String bucketName;
|
||||
private static String accessKey;
|
||||
private static String secretKey;
|
||||
private static Double imgSize;
|
||||
private static Double fileSize;
|
||||
|
||||
|
||||
private static final String SEPARATOR = "/";
|
||||
|
||||
public MinIOUtils() {
|
||||
}
|
||||
|
||||
public MinIOUtils(String endpoint, String bucketName, String accessKey, String secretKey, Double imgSize, Double fileSize) {
|
||||
MinIOUtils.endpoint = endpoint;
|
||||
MinIOUtils.bucketName = bucketName;
|
||||
MinIOUtils.accessKey = accessKey;
|
||||
MinIOUtils.secretKey = secretKey;
|
||||
MinIOUtils.imgSize = imgSize;
|
||||
MinIOUtils.fileSize = fileSize;
|
||||
createMinioClient();
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建基于Java端的MinioClient
|
||||
*/
|
||||
public void createMinioClient() {
|
||||
try {
|
||||
if (null == minioClient) {
|
||||
log.info("开始创建 MinioClient...");
|
||||
minioClient = MinioClient
|
||||
.builder()
|
||||
.endpoint(endpoint)
|
||||
.credentials(accessKey, secretKey)
|
||||
.build();
|
||||
createBucket(bucketName);
|
||||
log.info("创建完毕 MinioClient...");
|
||||
}
|
||||
} catch (Exception e) {
|
||||
log.error("MinIO服务器异常:{}", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取上传文件前缀路径
|
||||
* @return
|
||||
*/
|
||||
public static String getBasisUrl() {
|
||||
return endpoint + SEPARATOR + bucketName + SEPARATOR;
|
||||
}
|
||||
|
||||
/****************************** Operate Bucket Start ******************************/
|
||||
|
||||
/**
|
||||
* 启动SpringBoot容器的时候初始化Bucket
|
||||
* 如果没有Bucket则创建
|
||||
* @throws Exception
|
||||
*/
|
||||
private static void createBucket(String bucketName) throws Exception {
|
||||
if (!bucketExists(bucketName)) {
|
||||
minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断Bucket是否存在,true:存在,false:不存在
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static boolean bucketExists(String bucketName) throws Exception {
|
||||
return minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获得Bucket的策略
|
||||
* @param bucketName
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String getBucketPolicy(String bucketName) throws Exception {
|
||||
String bucketPolicy = minioClient
|
||||
.getBucketPolicy(
|
||||
GetBucketPolicyArgs
|
||||
.builder()
|
||||
.bucket(bucketName)
|
||||
.build()
|
||||
);
|
||||
return bucketPolicy;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获得所有Bucket列表
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static List<Bucket> getAllBuckets() throws Exception {
|
||||
return minioClient.listBuckets();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据bucketName获取其相关信息
|
||||
* @param bucketName
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static Optional<Bucket> getBucket(String bucketName) throws Exception {
|
||||
return getAllBuckets().stream().filter(b -> b.name().equals(bucketName)).findFirst();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据bucketName删除Bucket,true:删除成功; false:删除失败,文件或已不存在
|
||||
* @param bucketName
|
||||
* @throws Exception
|
||||
*/
|
||||
public static void removeBucket(String bucketName) throws Exception {
|
||||
minioClient.removeBucket(RemoveBucketArgs.builder().bucket(bucketName).build());
|
||||
}
|
||||
|
||||
/****************************** Operate Bucket End ******************************/
|
||||
|
||||
|
||||
/****************************** Operate Files Start ******************************/
|
||||
|
||||
/**
|
||||
* 判断文件是否存在
|
||||
* @param bucketName 存储桶
|
||||
* @param objectName 文件名
|
||||
* @return
|
||||
*/
|
||||
public static boolean isObjectExist(String bucketName, String objectName) {
|
||||
boolean exist = true;
|
||||
try {
|
||||
minioClient.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build());
|
||||
} catch (Exception e) {
|
||||
exist = false;
|
||||
}
|
||||
return exist;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断文件夹是否存在
|
||||
* @param bucketName 存储桶
|
||||
* @param objectName 文件夹名称
|
||||
* @return
|
||||
*/
|
||||
public static boolean isFolderExist(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() && objectName.equals(item.objectName())) {
|
||||
exist = true;
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
exist = false;
|
||||
}
|
||||
return exist;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件前缀查询文件
|
||||
* @param bucketName 存储桶
|
||||
* @param prefix 前缀
|
||||
* @param recursive 是否使用递归查询
|
||||
* @return MinioItem 列表
|
||||
* @throws Exception
|
||||
*/
|
||||
public static List<Item> getAllObjectsByPrefix(String bucketName,
|
||||
String prefix,
|
||||
boolean recursive) throws Exception {
|
||||
List<Item> list = new ArrayList<>();
|
||||
Iterable<Result<Item>> objectsIterator = minioClient.listObjects(
|
||||
ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(recursive).build());
|
||||
if (objectsIterator != null) {
|
||||
for (Result<Item> o : objectsIterator) {
|
||||
Item item = o.get();
|
||||
list.add(item);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件流
|
||||
* @param bucketName 存储桶
|
||||
* @param objectName 文件名
|
||||
* @return 二进制流
|
||||
*/
|
||||
public static InputStream getObject(String bucketName, String objectName) throws Exception {
|
||||
return minioClient.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 断点下载
|
||||
* @param bucketName 存储桶
|
||||
* @param objectName 文件名称
|
||||
* @param offset 起始字节的位置
|
||||
* @param length 要读取的长度
|
||||
* @return 二进制流
|
||||
*/
|
||||
public InputStream getObject(String bucketName, String objectName, long offset, long length)throws Exception {
|
||||
return minioClient.getObject(
|
||||
GetObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.offset(offset)
|
||||
.length(length)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取路径下文件列表
|
||||
* @param bucketName 存储桶
|
||||
* @param prefix 文件名称
|
||||
* @param recursive 是否递归查找,false:模拟文件夹结构查找
|
||||
* @return 二进制流
|
||||
*/
|
||||
public static Iterable<Result<Item>> listObjects(String bucketName, String prefix,
|
||||
boolean recursive) {
|
||||
return minioClient.listObjects(
|
||||
ListObjectsArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.prefix(prefix)
|
||||
.recursive(recursive)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用MultipartFile进行文件上传
|
||||
* @param bucketName 存储桶
|
||||
* @param file 文件名
|
||||
* @param objectName 对象名
|
||||
* @param contentType 类型
|
||||
* @return
|
||||
* @throws Exception
|
||||
*/
|
||||
public static ObjectWriteResponse uploadFile(String bucketName, MultipartFile file,
|
||||
String objectName, String contentType) throws Exception {
|
||||
InputStream inputStream = file.getInputStream();
|
||||
return minioClient.putObject(
|
||||
PutObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.contentType(contentType)
|
||||
.stream(inputStream, inputStream.available(), -1)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传本地文件
|
||||
* @param bucketName 存储桶
|
||||
* @param objectName 对象名称
|
||||
* @param fileName 本地文件路径
|
||||
*/
|
||||
public static ObjectWriteResponse uploadFile(String bucketName, String objectName,
|
||||
String fileName) throws Exception {
|
||||
return minioClient.uploadObject(
|
||||
UploadObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.filename(fileName)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过流上传文件
|
||||
*
|
||||
* @param bucketName 存储桶
|
||||
* @param objectName 文件对象
|
||||
* @param inputStream 文件流
|
||||
*/
|
||||
public static ObjectWriteResponse uploadFile(String bucketName, String objectName, InputStream inputStream) throws Exception {
|
||||
return minioClient.putObject(
|
||||
PutObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.stream(inputStream, inputStream.available(), -1)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建文件夹或目录
|
||||
* @param bucketName 存储桶
|
||||
* @param objectName 目录路径
|
||||
*/
|
||||
public static ObjectWriteResponse createDir(String bucketName, String objectName) throws Exception {
|
||||
return minioClient.putObject(
|
||||
PutObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.stream(new ByteArrayInputStream(new byte[]{}), 0, -1)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件信息, 如果抛出异常则说明文件不存在
|
||||
*
|
||||
* @param bucketName 存储桶
|
||||
* @param objectName 文件名称
|
||||
*/
|
||||
public static String getFileStatusInfo(String bucketName, String objectName) throws Exception {
|
||||
return minioClient.statObject(
|
||||
StatObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.build()).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 拷贝文件
|
||||
*
|
||||
* @param bucketName 存储桶
|
||||
* @param objectName 文件名
|
||||
* @param srcBucketName 目标存储桶
|
||||
* @param srcObjectName 目标文件名
|
||||
*/
|
||||
public static ObjectWriteResponse copyFile(String bucketName, String objectName,
|
||||
String srcBucketName, String srcObjectName) throws Exception {
|
||||
return minioClient.copyObject(
|
||||
CopyObjectArgs.builder()
|
||||
.source(CopySource.builder().bucket(bucketName).object(objectName).build())
|
||||
.bucket(srcBucketName)
|
||||
.object(srcObjectName)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
* @param bucketName 存储桶
|
||||
* @param objectName 文件名称
|
||||
*/
|
||||
public static void removeFile(String bucketName, String objectName) throws Exception {
|
||||
minioClient.removeObject(
|
||||
RemoveObjectArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量删除文件
|
||||
* @param bucketName 存储桶
|
||||
* @param keys 需要删除的文件列表
|
||||
* @return
|
||||
*/
|
||||
public static void removeFiles(String bucketName, List<String> keys) {
|
||||
List<DeleteObject> objects = new LinkedList<>();
|
||||
keys.forEach(s -> {
|
||||
objects.add(new DeleteObject(s));
|
||||
try {
|
||||
removeFile(bucketName, s);
|
||||
} catch (Exception e) {
|
||||
log.error("批量删除失败!error:{}",e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件外链
|
||||
* @param bucketName 存储桶
|
||||
* @param objectName 文件名
|
||||
* @param expires 过期时间 <=7 秒 (外链有效时间(单位:秒))
|
||||
* @return url
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String getPresignedObjectUrl(String bucketName, String objectName, Integer expires) throws Exception {
|
||||
GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder().expiry(expires).bucket(bucketName).object(objectName).build();
|
||||
return minioClient.getPresignedObjectUrl(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得文件外链
|
||||
* @param bucketName
|
||||
* @param objectName
|
||||
* @return url
|
||||
* @throws Exception
|
||||
*/
|
||||
public static String getPresignedObjectUrl(String bucketName, String objectName) throws Exception {
|
||||
GetPresignedObjectUrlArgs args = GetPresignedObjectUrlArgs.builder()
|
||||
.bucket(bucketName)
|
||||
.object(objectName)
|
||||
.method(Method.GET).build();
|
||||
return minioClient.getPresignedObjectUrl(args);
|
||||
}
|
||||
|
||||
/**
|
||||
* 将URLDecoder编码转成UTF8
|
||||
* @param str
|
||||
* @return
|
||||
* @throws UnsupportedEncodingException
|
||||
*/
|
||||
public static String getUtf8ByURLDecoder(String str) throws UnsupportedEncodingException {
|
||||
String url = str.replaceAll("%(?![0-9a-fA-F]{2})", "%25");
|
||||
return URLDecoder.decode(url, "UTF-8");
|
||||
}
|
||||
|
||||
/****************************** Operate Files End ******************************/
|
||||
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,68 @@
|
||||
package com.schisandra.oss.application.oss.utils;
|
||||
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* @author landaiqing
|
||||
* @version PathUtil.java, v 1.1 2022/2/18 17:01 chenmin Exp $
|
||||
* Created on 2022/2/18
|
||||
*/
|
||||
@Slf4j
|
||||
public class OssPathUtil {
|
||||
|
||||
public static String valid(String basePath) {
|
||||
// 根路径未配置时,默认路径为 /
|
||||
if (ObjectUtil.isEmpty(basePath)) {
|
||||
basePath = StrUtil.SLASH;
|
||||
}
|
||||
// 将路径分隔符统一转为 /
|
||||
basePath = basePath.replaceAll("\\\\", StrUtil.SLASH).replaceAll("//", StrUtil.SLASH);
|
||||
|
||||
// 将配置默认转为绝对路径
|
||||
if (!basePath.startsWith(StrUtil.SLASH)) {
|
||||
basePath = StrUtil.SLASH + basePath;
|
||||
}
|
||||
if (!basePath.endsWith(StrUtil.SLASH)) {
|
||||
basePath = basePath + StrUtil.SLASH;
|
||||
}
|
||||
return basePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* 路径转换
|
||||
* 将路径分隔符转为统一的 / 分隔
|
||||
* @param key 路径
|
||||
* @param isAbsolute 是否绝对路径
|
||||
* true:绝对路径;false:相对路径
|
||||
* @return 以 / 为分隔的路径
|
||||
*/
|
||||
public static String convertPath(String key, Boolean isAbsolute) {
|
||||
key = key.replaceAll("\\\\", StrUtil.SLASH).replaceAll("//", StrUtil.SLASH);
|
||||
if (isAbsolute && !key.startsWith(StrUtil.SLASH)) {
|
||||
key = StrUtil.SLASH + key;
|
||||
} else if (!isAbsolute && key.startsWith(StrUtil.SLASH)) {
|
||||
key = key.replaceFirst(StrUtil.SLASH, "");
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取相对根路径的绝对路径
|
||||
* @param path 全路径
|
||||
* @param basePath 根路径
|
||||
* @param isAbsolute 是否绝对路径
|
||||
* true:绝对路径;false:相对路径
|
||||
* @return 完整路径
|
||||
*/
|
||||
public static String replaceKey(String path, String basePath, Boolean isAbsolute) {
|
||||
String newPath;
|
||||
if (StrUtil.SLASH.equals(basePath)) {
|
||||
newPath = convertPath(path, isAbsolute);
|
||||
} else {
|
||||
newPath = convertPath(path, isAbsolute).replaceAll(convertPath(basePath, isAbsolute), "");
|
||||
}
|
||||
return convertPath(newPath, isAbsolute);
|
||||
}
|
||||
}
|
@@ -100,7 +100,24 @@
|
||||
<artifactId>caffeine</artifactId>
|
||||
<version>3.1.8</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.hutool</groupId>
|
||||
<artifactId>hutool-all</artifactId>
|
||||
<version>5.1.1</version>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>io.minio</groupId>
|
||||
<artifactId>minio</artifactId>
|
||||
<version>8.2.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>net.coobird</groupId>
|
||||
<artifactId>thumbnailator</artifactId>
|
||||
<version>0.4.8</version>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
</project>
|
||||
|
@@ -0,0 +1,194 @@
|
||||
package com.schisandra.oss.common.utils;
|
||||
|
||||
import net.coobird.thumbnailator.Thumbnails;
|
||||
import net.coobird.thumbnailator.geometry.Positions;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.TextAttribute;
|
||||
import java.awt.geom.AffineTransform;
|
||||
import java.awt.geom.Rectangle2D;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.AttributedCharacterIterator;
|
||||
import java.text.AttributedString;
|
||||
|
||||
/**
|
||||
* 图像工具类
|
||||
*/
|
||||
public class ImageUtil {
|
||||
private static final Logger log = LoggerFactory.getLogger(ImageUtil.class);
|
||||
//压缩率
|
||||
private static final transient float IMAGE_RATIO = 0.1f;
|
||||
//压缩最大宽度
|
||||
private static final transient int IMAGE_WIDTH = 800;
|
||||
// 水印透明度
|
||||
private static float alpha = 0.3f;
|
||||
// 水印文字字体
|
||||
private static Font font = new Font("PingFang SC Regular", Font.PLAIN, 36);
|
||||
// 水印文字颜色
|
||||
private static Color color = new Color(111, 111, 111);
|
||||
//水印文字内容
|
||||
private static final String text = "这是一个水印文本";
|
||||
// 水印之间的间隔
|
||||
private static final int XMOVE = 80;
|
||||
// 水印之间的间隔
|
||||
private static final int YMOVE = 80;
|
||||
|
||||
/**
|
||||
* 压缩图像
|
||||
*
|
||||
* @param image
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static BufferedImage compress(BufferedImage image) throws IOException {
|
||||
Thumbnails.Builder<BufferedImage> imageBuilder = Thumbnails.of(image).outputQuality(IMAGE_RATIO);
|
||||
if (image.getWidth() > IMAGE_WIDTH) {
|
||||
return imageBuilder.width(IMAGE_WIDTH).asBufferedImage();
|
||||
} else {
|
||||
return imageBuilder.scale(1).asBufferedImage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 图像添加水印
|
||||
*
|
||||
* @param
|
||||
* @return
|
||||
*/
|
||||
public static BufferedImage setWatermark(BufferedImage image) throws IOException {
|
||||
return Thumbnails.of(image)
|
||||
.outputQuality(IMAGE_RATIO)
|
||||
.scale(1)
|
||||
.watermark(Positions.BOTTOM_RIGHT
|
||||
, createWatermark(text
|
||||
, image.getWidth()
|
||||
, image.getHeight()
|
||||
)
|
||||
, alpha)
|
||||
.asBufferedImage();
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据文件扩展名判断文件是否图片格式
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public static boolean isImage(String fileName) {
|
||||
String[] imageExtension = new String[]{"jpeg", "jpg", "gif", "bmp", "png"};
|
||||
|
||||
for (String e : imageExtension) if (getFileExtention(fileName).toLowerCase().equals(e)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取文件后缀名称
|
||||
*
|
||||
* @param fileName
|
||||
* @return
|
||||
*/
|
||||
public static String getFileExtention(String fileName) {
|
||||
String extension = fileName.substring(fileName.lastIndexOf(".") + 1);
|
||||
return extension;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据图片对象获取对应InputStream
|
||||
*
|
||||
* @param image
|
||||
* @param readImageFormat
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static InputStream getInputStream(BufferedImage image, String readImageFormat) throws IOException {
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
ImageIO.write(image, readImageFormat, os);
|
||||
InputStream is = new ByteArrayInputStream(os.toByteArray());
|
||||
os.close();
|
||||
return is;
|
||||
}
|
||||
|
||||
/**
|
||||
* 创建水印图片
|
||||
*
|
||||
* @param text 水印文字
|
||||
* @param width 图片宽
|
||||
* @param height 图片高
|
||||
* @return
|
||||
*/
|
||||
public static BufferedImage createWatermark(String text, int width, int height) {
|
||||
BufferedImage image = new BufferedImage(width
|
||||
, height, BufferedImage.TYPE_INT_RGB);
|
||||
// 2.获取图片画笔
|
||||
Graphics2D g = image.createGraphics();
|
||||
// ---------- 增加下面的代码使得背景透明 -----------------
|
||||
image = g.getDeviceConfiguration().createCompatibleImage(width, height, Transparency.TRANSLUCENT);
|
||||
g.dispose();
|
||||
g = image.createGraphics();
|
||||
// ---------- 背景透明代码结束 -----------------
|
||||
// 6、处理文字
|
||||
AttributedString ats = new AttributedString(text);
|
||||
ats.addAttribute(TextAttribute.FONT, font, 0, text.length());
|
||||
AttributedCharacterIterator iter = ats.getIterator();
|
||||
// 7、设置对线段的锯齿状边缘处理
|
||||
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
|
||||
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
|
||||
// 8、设置水印旋转
|
||||
g.rotate(Math.toRadians(-30));
|
||||
// 9、设置水印文字颜色
|
||||
g.setColor(color);
|
||||
// 10、设置水印文字Font
|
||||
g.setFont(font);
|
||||
// 11、设置水印文字透明度
|
||||
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_ATOP, alpha));
|
||||
g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER));
|
||||
/**
|
||||
* 水印铺满图片
|
||||
* 计算水印位置
|
||||
*/
|
||||
int x = -width / 2;
|
||||
int y = -height / 2;
|
||||
int[] arr = getWidthAndHeight(text, font);
|
||||
int markWidth = arr[0];// 字体长度
|
||||
int markHeight = arr[1];// 字体高度
|
||||
// 循环添加水印
|
||||
while (x < width * 1.5) {
|
||||
y = -height / 2;
|
||||
while (y < height * 1.5) {
|
||||
g.drawString(text, x, y);
|
||||
|
||||
y += markHeight + YMOVE;
|
||||
}
|
||||
x += markWidth + XMOVE;
|
||||
}
|
||||
// 13、释放资源
|
||||
g.dispose();
|
||||
return image;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算字体宽度及高度
|
||||
*
|
||||
* @param text
|
||||
* @param font
|
||||
* @return
|
||||
*/
|
||||
private static int[] getWidthAndHeight(String text, Font font) {
|
||||
Rectangle2D r = font.getStringBounds(text, new FontRenderContext(
|
||||
AffineTransform.getScaleInstance(1, 1), false, false));
|
||||
int unitHeight = (int) Math.floor(r.getHeight());//
|
||||
// 获取整个str用了font样式的宽度这里用四舍五入后+1保证宽度绝对能容纳这个字符串作为图片的宽度
|
||||
int width = (int) Math.round(r.getWidth()) + 1;
|
||||
// 把单个字符的高度+3保证高度绝对能容纳字符串作为图片的高度
|
||||
int height = unitHeight + 3;
|
||||
return new int[]{width, height};
|
||||
}
|
||||
}
|
@@ -18,5 +18,6 @@ public interface SchisandraOssConfigBOConverter {
|
||||
SchisandraOssConfigBOConverter INSTANCE = Mappers.getMapper(SchisandraOssConfigBOConverter.class);
|
||||
|
||||
SchisandraOssConfig convertBOToEntity(SchisandraOssConfigBO schisandraOssConfigBO);
|
||||
SchisandraOssConfigBO convertEntityToBO(SchisandraOssConfig schisandraOssConfig);
|
||||
|
||||
}
|
||||
|
@@ -25,4 +25,5 @@ public interface SchisandraOssConfigDomainService {
|
||||
*/
|
||||
Boolean delete(SchisandraOssConfigBO schisandraOssConfigBO);
|
||||
|
||||
SchisandraOssConfigBO getOssConfig(String minio);
|
||||
}
|
||||
|
@@ -4,7 +4,6 @@ package com.schisandra.oss.domain.service.impl;
|
||||
import com.schisandra.oss.common.enums.IsDeletedFlagEnum;
|
||||
import com.schisandra.oss.domain.convert.SchisandraOssConfigBOConverter;
|
||||
import com.schisandra.oss.domain.bo.SchisandraOssConfigBO;
|
||||
import com.schisandra.oss.domain.service.SchisandraOssConfigDomainService;
|
||||
import com.schisandra.oss.infra.basic.entity.SchisandraOssConfig;
|
||||
import com.schisandra.oss.infra.basic.service.SchisandraOssConfigService;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -20,7 +19,7 @@ import javax.annotation.Resource;
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
public class SchisandraOssConfigDomainServiceImpl implements SchisandraOssConfigDomainService {
|
||||
public class SchisandraOssConfigDomainService implements com.schisandra.oss.domain.service.SchisandraOssConfigDomainService {
|
||||
|
||||
@Resource
|
||||
private SchisandraOssConfigService schisandraOssConfigService;
|
||||
@@ -46,4 +45,11 @@ public class SchisandraOssConfigDomainServiceImpl implements SchisandraOssConfig
|
||||
return schisandraOssConfigService.update(schisandraOssConfig) > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SchisandraOssConfigBO getOssConfig(String ossType) {
|
||||
SchisandraOssConfig ossConfig = schisandraOssConfigService.getOssConfig(ossType);
|
||||
SchisandraOssConfigBO schisandraOssConfigBO = SchisandraOssConfigBOConverter.INSTANCE.convertEntityToBO(ossConfig);
|
||||
return schisandraOssConfigBO;
|
||||
}
|
||||
|
||||
}
|
@@ -47,4 +47,5 @@ public interface SchisandraOssConfigService {
|
||||
*/
|
||||
SchisandraOssConfig queryByCondition(SchisandraOssConfig schisandraOssConfig);
|
||||
|
||||
SchisandraOssConfig getOssConfig(String ossType);
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package com.schisandra.oss.infra.basic.service.impl;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
|
||||
import com.schisandra.oss.infra.basic.dao.SchisandraOssConfigDao;
|
||||
import com.schisandra.oss.infra.basic.entity.SchisandraOssConfig;
|
||||
@@ -111,4 +112,11 @@ public class SchisandraOssConfigServiceImpl implements SchisandraOssConfigServic
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public SchisandraOssConfig getOssConfig(String ossType) {
|
||||
SchisandraOssConfig ossConfig = schisandraOssConfigDao.selectOne(new QueryWrapper<SchisandraOssConfig>()
|
||||
.eq("oss_type", ossType).eq("is_deleted", 0));
|
||||
return ossConfig;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -39,6 +39,14 @@
|
||||
<artifactId>spring-cloud-starter-bootstrap</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.alibaba.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.schisandra</groupId>
|
||||
|
Reference in New Issue
Block a user