mirror of
https://gitee.com/lab1024/smart-admin.git
synced 2026-03-03 22:14:25 +08:00
v3.30.0 【增加】字典项增加回显样式;【优化】文件S3协议对最新minio的支持;
This commit is contained in:
@@ -15,6 +15,7 @@ import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
|
||||
import software.amazon.awssdk.regions.Region;
|
||||
import software.amazon.awssdk.services.s3.S3Client;
|
||||
import software.amazon.awssdk.services.s3.S3Configuration;
|
||||
import software.amazon.awssdk.services.s3.presigner.S3Presigner;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
@@ -31,55 +32,70 @@ import java.net.URI;
|
||||
@Configuration
|
||||
public class FileConfig implements WebMvcConfigurer {
|
||||
|
||||
private static final String HTTPS = "https://";
|
||||
|
||||
private static final String HTTP = "http://";
|
||||
|
||||
private static final String MODE_CLOUD = "cloud";
|
||||
|
||||
private static final String MODE_LOCAL = "local";
|
||||
|
||||
@Value("${file.storage.cloud.region}")
|
||||
private String region;
|
||||
|
||||
@Value("${file.storage.cloud.endpoint}")
|
||||
private String endpoint;
|
||||
|
||||
@Value("${file.storage.cloud.bucket-name}")
|
||||
private String bucketName;
|
||||
|
||||
@Value("${file.storage.cloud.access-key}")
|
||||
private String accessKey;
|
||||
|
||||
@Value("${file.storage.cloud.secret-key}")
|
||||
private String secretKey;
|
||||
|
||||
@Value("${file.storage.cloud.private-url-expire-seconds}")
|
||||
private Long privateUrlExpireSeconds;
|
||||
|
||||
@Value("${file.storage.cloud.url-prefix}")
|
||||
private String urlPrefix;
|
||||
|
||||
@Value("${file.storage.local.upload-path}")
|
||||
private String uploadPath;
|
||||
|
||||
@Value("${file.storage.mode}")
|
||||
private String mode;
|
||||
|
||||
@Value("${file.storage.cloud.region}")
|
||||
private String cloudRegion;
|
||||
|
||||
@Value("${file.storage.cloud.endpoint}")
|
||||
private String cloudEndpoint;
|
||||
|
||||
@Value("${file.storage.cloud.bucket-name}")
|
||||
private String cloudBucketName;
|
||||
|
||||
@Value("${file.storage.cloud.access-key}")
|
||||
private String cloudAccessKey;
|
||||
|
||||
@Value("${file.storage.cloud.secret-key}")
|
||||
private String cloudSecretKey;
|
||||
|
||||
@Value("${file.storage.cloud.private-url-expire-seconds}")
|
||||
private Long cloudPrivateUrlExpireSeconds;
|
||||
|
||||
@Value("${file.storage.cloud.public-url-prefix}")
|
||||
private String cloudPublicUrlPrefix;
|
||||
|
||||
@Value("${file.storage.local.upload-path}")
|
||||
private String localUploadPath;
|
||||
|
||||
|
||||
/**
|
||||
* 初始化 云oss client 配置
|
||||
*
|
||||
* @return
|
||||
* 初始化 s3 client 配置
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "file.storage", name = {"mode"}, havingValue = MODE_CLOUD)
|
||||
public S3Client initAmazonS3() {
|
||||
public S3Client initS3Client() {
|
||||
return S3Client.builder()
|
||||
.region(Region.AWS_GLOBAL)
|
||||
.endpointOverride(URI.create((urlPrefix.startsWith(HTTPS) ? HTTPS : HTTP) + endpoint))
|
||||
.region(Region.of(cloudRegion))
|
||||
.endpointOverride(URI.create(cloudEndpoint))
|
||||
.credentialsProvider(
|
||||
StaticCredentialsProvider.create(
|
||||
AwsBasicCredentials.create(accessKey, secretKey)))
|
||||
AwsBasicCredentials.create(cloudAccessKey, cloudSecretKey)))
|
||||
.serviceConfiguration(S3Configuration.builder()
|
||||
.pathStyleAccessEnabled(false)
|
||||
.chunkedEncodingEnabled(false)
|
||||
.build())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化 s3 预签名
|
||||
*/
|
||||
@Bean
|
||||
@ConditionalOnProperty(prefix = "file.storage", name = {"mode"}, havingValue = MODE_CLOUD)
|
||||
public S3Presigner initS3Presigner() {
|
||||
return S3Presigner
|
||||
.builder()
|
||||
.region(Region.of(cloudRegion))
|
||||
.endpointOverride(URI.create(cloudEndpoint))
|
||||
.credentialsProvider(
|
||||
StaticCredentialsProvider.create(
|
||||
AwsBasicCredentials.create(cloudAccessKey, cloudSecretKey)))
|
||||
.serviceConfiguration(S3Configuration.builder()
|
||||
.pathStyleAccessEnabled(false)
|
||||
.chunkedEncodingEnabled(false)
|
||||
@@ -102,7 +118,7 @@ public class FileConfig implements WebMvcConfigurer {
|
||||
@Override
|
||||
public void addResourceHandlers(ResourceHandlerRegistry registry) {
|
||||
if (MODE_LOCAL.equals(mode)) {
|
||||
String path = uploadPath.endsWith("/") ? uploadPath : uploadPath + "/";
|
||||
String path = localUploadPath.endsWith("/") ? localUploadPath : localUploadPath + "/";
|
||||
registry.addResourceHandler(FileStorageLocalServiceImpl.UPLOAD_MAPPING + "/**").addResourceLocations("file:" + path);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
package net.lab1024.sa.base.module.support.dict.constant;
|
||||
|
||||
import lombok.Getter;
|
||||
import net.lab1024.sa.base.common.enumeration.BaseEnum;
|
||||
|
||||
/**
|
||||
* 字典回显样式 枚举
|
||||
*/
|
||||
@Getter
|
||||
public enum DictDataStyleEnum implements BaseEnum {
|
||||
|
||||
DEFAULT("默认", "default"),
|
||||
PRIMARY("主要", "primary"),
|
||||
SUCCESS("成功", "success"),
|
||||
INFO("信息", "info"),
|
||||
WARNING("警告", "warning"),
|
||||
DANGER("危险", "danger");
|
||||
|
||||
private final String value;
|
||||
|
||||
private final String desc;
|
||||
|
||||
DictDataStyleEnum(String desc, String value) {
|
||||
this.desc = desc;
|
||||
this.value = value;
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,11 @@ public class DictDataEntity {
|
||||
*/
|
||||
private String dataLabel;
|
||||
|
||||
/**
|
||||
* 字典项样式
|
||||
*/
|
||||
private String dataStyle;
|
||||
|
||||
/**
|
||||
* 备注
|
||||
*/
|
||||
|
||||
@@ -4,6 +4,10 @@ import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import net.lab1024.sa.base.common.swagger.SchemaEnum;
|
||||
import net.lab1024.sa.base.common.validator.enumeration.CheckEnum;
|
||||
import net.lab1024.sa.base.module.support.dict.constant.DictDataStyleEnum;
|
||||
|
||||
|
||||
/**
|
||||
* 字典数据表 新建表单
|
||||
@@ -28,6 +32,10 @@ public class DictDataAddForm {
|
||||
@NotBlank(message = "字典项显示名称 不能为空")
|
||||
private String dataLabel;
|
||||
|
||||
@SchemaEnum(value = DictDataStyleEnum.class, desc = "数据样式")
|
||||
@CheckEnum(message = "样式参数错误", value = DictDataStyleEnum.class)
|
||||
private String dataStyle;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
|
||||
@@ -38,6 +38,9 @@ public class DictDataVO implements Serializable {
|
||||
@Schema(description = "字典项显示名称")
|
||||
private String dataLabel;
|
||||
|
||||
@Schema(description = "字典项回显")
|
||||
private String dataStyle;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
|
||||
@@ -20,22 +20,22 @@ public enum FileFolderTypeEnum implements BaseEnum {
|
||||
/**
|
||||
* 通用
|
||||
*/
|
||||
COMMON(1, FileFolderTypeEnum.FOLDER_PUBLIC + "/common/", "通用"),
|
||||
COMMON(1, FileFolderTypeEnum.FOLDER_PRIVATE + "/common/", "通用"),
|
||||
|
||||
/**
|
||||
* 公告
|
||||
*/
|
||||
NOTICE(2, FileFolderTypeEnum.FOLDER_PUBLIC + "/notice/", "公告"),
|
||||
NOTICE(2, FileFolderTypeEnum.FOLDER_PRIVATE + "/notice/", "公告"),
|
||||
|
||||
/**
|
||||
* 帮助中心
|
||||
*/
|
||||
HELP_DOC(3, FileFolderTypeEnum.FOLDER_PUBLIC + "/help-doc/", "帮助中心"),
|
||||
HELP_DOC(3, FileFolderTypeEnum.FOLDER_PRIVATE + "/help-doc/", "帮助中心"),
|
||||
|
||||
/**
|
||||
* 意见反馈
|
||||
*/
|
||||
FEEDBACK(4, FileFolderTypeEnum.FOLDER_PUBLIC + "/feedback/", "意见反馈"),
|
||||
FEEDBACK(4, FileFolderTypeEnum.FOLDER_PRIVATE + "/feedback/", "意见反馈"),
|
||||
|
||||
;
|
||||
|
||||
|
||||
@@ -71,6 +71,9 @@ public class FileStorageCloudServiceImpl implements IFileStorageService {
|
||||
@Resource
|
||||
private S3Client s3Client;
|
||||
|
||||
@Resource
|
||||
private S3Presigner s3Presigner;
|
||||
|
||||
@Resource
|
||||
private FileConfig cloudConfig;
|
||||
|
||||
@@ -104,7 +107,7 @@ public class FileStorageCloudServiceImpl implements IFileStorageService {
|
||||
// 根据文件路径获取并设置访问权限
|
||||
ObjectCannedACL acl = this.getACL(path);
|
||||
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
|
||||
.bucket(cloudConfig.getBucketName())
|
||||
.bucket(cloudConfig.getCloudBucketName())
|
||||
.key(fileKey)
|
||||
.metadata(userMetadata)
|
||||
.contentLength(file.getSize())
|
||||
@@ -128,7 +131,7 @@ public class FileStorageCloudServiceImpl implements IFileStorageService {
|
||||
uploadVO.setFileName(originalFileName);
|
||||
uploadVO.setFileType(fileType);
|
||||
// 根据 访问权限 返回不同的 URL
|
||||
String url = cloudConfig.getUrlPrefix() + fileKey;
|
||||
String url = cloudConfig.getCloudPublicUrlPrefix() + fileKey;
|
||||
if (ObjectCannedACL.PRIVATE.equals(acl)) {
|
||||
// 获取临时访问的URL
|
||||
url = this.getFileUrl(fileKey).getData();
|
||||
@@ -153,11 +156,10 @@ public class FileStorageCloudServiceImpl implements IFileStorageService {
|
||||
|
||||
if (!fileKey.startsWith(FileFolderTypeEnum.FOLDER_PRIVATE)) {
|
||||
// 不是私有的 都公共读
|
||||
return ResponseDTO.ok(cloudConfig.getUrlPrefix() + fileKey);
|
||||
return ResponseDTO.ok(cloudConfig.getCloudPublicUrlPrefix() + fileKey);
|
||||
}
|
||||
|
||||
// 如果是私有的,则规定时间内可以访问,超过规定时间,则连接失效
|
||||
|
||||
String fileRedisKey = RedisKeyConst.Support.FILE_PRIVATE_VO + fileKey;
|
||||
FileVO fileVO = redisService.getObject(fileRedisKey, FileVO.class);
|
||||
if (fileVO == null) {
|
||||
@@ -165,15 +167,22 @@ public class FileStorageCloudServiceImpl implements IFileStorageService {
|
||||
if (fileVO == null) {
|
||||
return ResponseDTO.userErrorParam("文件不存在");
|
||||
}
|
||||
GetObjectRequest getUrlRequest = GetObjectRequest.builder().bucket(cloudConfig.getBucketName()).key(fileKey).build();
|
||||
GetObjectPresignRequest getObjectPresignRequest = GetObjectPresignRequest.builder().signatureDuration(Duration.ofSeconds(cloudConfig.getPrivateUrlExpireSeconds())).getObjectRequest(getUrlRequest).build();
|
||||
GetObjectRequest getUrlRequest = GetObjectRequest
|
||||
.builder()
|
||||
.bucket(cloudConfig.getCloudBucketName())
|
||||
.key(fileKey)
|
||||
.build();
|
||||
|
||||
S3Presigner presigner = S3Presigner.builder().region(Region.of(cloudConfig.getRegion())).build();
|
||||
GetObjectPresignRequest getObjectPresignRequest = GetObjectPresignRequest
|
||||
.builder()
|
||||
.signatureDuration(Duration.ofSeconds(cloudConfig.getCloudPrivateUrlExpireSeconds()))
|
||||
.getObjectRequest(getUrlRequest)
|
||||
.build();
|
||||
|
||||
PresignedGetObjectRequest presignedGetObjectRequest = presigner.presignGetObject(getObjectPresignRequest);
|
||||
PresignedGetObjectRequest presignedGetObjectRequest = s3Presigner.presignGetObject(getObjectPresignRequest);
|
||||
String url = presignedGetObjectRequest.url().toString();
|
||||
fileVO.setFileUrl(url);
|
||||
redisService.set(fileRedisKey, fileVO, cloudConfig.getPrivateUrlExpireSeconds() - 5);
|
||||
redisService.set(fileRedisKey, fileVO, cloudConfig.getCloudPrivateUrlExpireSeconds() - 5);
|
||||
}
|
||||
|
||||
return ResponseDTO.ok(fileVO.getFileUrl());
|
||||
@@ -187,7 +196,7 @@ public class FileStorageCloudServiceImpl implements IFileStorageService {
|
||||
public ResponseDTO<FileDownloadVO> download(String key) {
|
||||
|
||||
// 获取文件 meta
|
||||
HeadObjectRequest objectRequest = HeadObjectRequest.builder().bucket(this.cloudConfig.getBucketName()).key(key).build();
|
||||
HeadObjectRequest objectRequest = HeadObjectRequest.builder().bucket(this.cloudConfig.getCloudBucketName()).key(key).build();
|
||||
HeadObjectResponse headObjectResponse = s3Client.headObject(objectRequest);
|
||||
Map<String, String> userMetadata = headObjectResponse.metadata();
|
||||
FileMetadataVO metadataDTO = null;
|
||||
@@ -201,7 +210,7 @@ public class FileStorageCloudServiceImpl implements IFileStorageService {
|
||||
}
|
||||
|
||||
//获取oss对象
|
||||
GetObjectRequest getObjectRequest = GetObjectRequest.builder().bucket(cloudConfig.getBucketName()).key(key).build();
|
||||
GetObjectRequest getObjectRequest = GetObjectRequest.builder().bucket(cloudConfig.getCloudBucketName()).key(key).build();
|
||||
ResponseBytes<GetObjectResponse> s3ClientObject = s3Client.getObject(getObjectRequest, ResponseTransformer.toBytes());
|
||||
|
||||
// 输入流转换为字节流
|
||||
@@ -236,7 +245,7 @@ public class FileStorageCloudServiceImpl implements IFileStorageService {
|
||||
*/
|
||||
@Override
|
||||
public ResponseDTO<String> delete(String fileKey) {
|
||||
DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder().bucket(cloudConfig.getBucketName()).key(fileKey).build();
|
||||
DeleteObjectRequest deleteObjectRequest = DeleteObjectRequest.builder().bucket(cloudConfig.getCloudBucketName()).key(fileKey).build();
|
||||
s3Client.deleteObject(deleteObjectRequest);
|
||||
return ResponseDTO.ok();
|
||||
}
|
||||
|
||||
@@ -103,12 +103,13 @@ file:
|
||||
url-prefix:
|
||||
cloud:
|
||||
region: oss-cn-hangzhou
|
||||
endpoint: oss-cn-hangzhou.aliyuncs.com
|
||||
endpoint: https://oss-cn-hangzhou.aliyuncs.com
|
||||
bucket-name: 1024lab-smart-admin
|
||||
access-key:
|
||||
secret-key:
|
||||
url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/
|
||||
private-url-expire-seconds: 3600
|
||||
# 云计算厂商支持公开的文件访问模式;minio默认是不支持的,对于minio用户可以配置为空
|
||||
public-url-prefix: https://1024lab-smart-admin.oss-cn-hangzhou.aliyuncs.com/
|
||||
|
||||
# open api配置
|
||||
springdoc:
|
||||
|
||||
@@ -103,12 +103,13 @@ file:
|
||||
url-prefix:
|
||||
cloud:
|
||||
region: oss-cn-hangzhou
|
||||
endpoint: oss-cn-hangzhou.aliyuncs.com
|
||||
endpoint: https://oss-cn-hangzhou.aliyuncs.com
|
||||
bucket-name: 1024lab-smart-admin
|
||||
access-key:
|
||||
secret-key:
|
||||
url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/
|
||||
private-url-expire-seconds: 3600
|
||||
# 云计算厂商支持公开的文件访问模式;minio默认是不支持的,对于minio用户可以配置为空
|
||||
public-url-prefix: https://1024lab-smart-admin.oss-cn-hangzhou.aliyuncs.com/
|
||||
|
||||
# open api配置
|
||||
springdoc:
|
||||
|
||||
@@ -103,12 +103,13 @@ file:
|
||||
url-prefix:
|
||||
cloud:
|
||||
region: oss-cn-hangzhou
|
||||
endpoint: oss-cn-hangzhou.aliyuncs.com
|
||||
endpoint: https://oss-cn-hangzhou.aliyuncs.com
|
||||
bucket-name: 1024lab-smart-admin
|
||||
access-key:
|
||||
secret-key:
|
||||
url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/
|
||||
private-url-expire-seconds: 3600
|
||||
# 云计算厂商支持公开的文件访问模式;minio默认是不支持的,对于minio用户可以配置为空
|
||||
public-url-prefix: https://1024lab-smart-admin.oss-cn-hangzhou.aliyuncs.com/
|
||||
|
||||
# open api配置
|
||||
springdoc:
|
||||
|
||||
@@ -103,12 +103,13 @@ file:
|
||||
url-prefix:
|
||||
cloud:
|
||||
region: oss-cn-hangzhou
|
||||
endpoint: oss-cn-hangzhou.aliyuncs.com
|
||||
endpoint: https://oss-cn-hangzhou.aliyuncs.com
|
||||
bucket-name: 1024lab-smart-admin
|
||||
access-key:
|
||||
secret-key:
|
||||
url-prefix: https://${file.storage.cloud.bucket-name}.${file.storage.cloud.endpoint}/
|
||||
private-url-expire-seconds: 3600
|
||||
# 云计算厂商支持公开的文件访问模式;minio默认是不支持的,对于minio用户可以配置为空
|
||||
public-url-prefix: https://1024lab-smart-admin.oss-cn-hangzhou.aliyuncs.com/
|
||||
|
||||
# open api配置
|
||||
springdoc:
|
||||
|
||||
Reference in New Issue
Block a user