mirror of
https://github.com/continew-org/continew-starter.git
synced 2025-09-11 06:57:14 +08:00
refactor(storage): 新增存储模块 - 本地和 S3 两种存储模式
This commit is contained in:
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-storage</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>continew-starter-storage-core</artifactId>
|
||||
<description>ContiNew Starter 存储模块 - 核心模块</description>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<!--redisson 缓存模块-->
|
||||
<dependency>
|
||||
<groupId>top.continew</groupId>
|
||||
<artifactId>continew-starter-cache-redisson</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!--图片处理工具-主要用做图片缩略处理-->
|
||||
<dependency>
|
||||
<groupId>net.coobird</groupId>
|
||||
<artifactId>thumbnailator</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
||||
</project>
|
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.storage.constant;
|
||||
|
||||
/**
|
||||
* 存储 常量
|
||||
*
|
||||
* @author echo
|
||||
* @date 2024/12/16 19:09
|
||||
*/
|
||||
public class StorageConstant {
|
||||
|
||||
/**
|
||||
* 默认存储 Key
|
||||
*/
|
||||
public static final String DEFAULT_KEY = "storage:default_config";
|
||||
|
||||
/**
|
||||
* 云服务商 域名前缀
|
||||
* <p>目前只支持 阿里云-oss 华为云-obs 腾讯云-cos</p>
|
||||
*/
|
||||
public static final String[] CLOUD_SERVICE_PREFIX = new String[] {"oss", "cos", "obs"};
|
||||
|
||||
/**
|
||||
* 缩略图后缀
|
||||
*/
|
||||
public static final String SMALL_SUFFIX = "small";
|
||||
}
|
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.storage.dao;
|
||||
|
||||
import top.continew.starter.storage.model.resp.UploadResp;
|
||||
|
||||
/**
|
||||
* 存储记录持久层接口
|
||||
*
|
||||
* @author echo
|
||||
* @date 2024/12/17 16:49
|
||||
*/
|
||||
public interface StorageDao {
|
||||
|
||||
/**
|
||||
* 记录上传信息
|
||||
*
|
||||
* @param uploadResp 上传信息
|
||||
*/
|
||||
void add(UploadResp uploadResp);
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.storage.dao.impl;
|
||||
|
||||
import top.continew.starter.storage.dao.StorageDao;
|
||||
import top.continew.starter.storage.model.resp.UploadResp;
|
||||
|
||||
/**
|
||||
* 默认记录实现,此类并不能真正保存记录,只是用来脱离数据库运行,保证文件上传功能可以正常使用
|
||||
*
|
||||
* @author echo
|
||||
* @date 2024/12/18 08:48
|
||||
**/
|
||||
public class StorageDaoDefaultImpl implements StorageDao {
|
||||
@Override
|
||||
public void add(UploadResp uploadResp) {
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.storage.decorator;
|
||||
|
||||
import top.continew.starter.storage.model.resp.ThumbnailResp;
|
||||
import top.continew.starter.storage.model.resp.UploadResp;
|
||||
import top.continew.starter.storage.strategy.StorageStrategy;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* 装饰器基类 - 用于重写
|
||||
*
|
||||
* @author echo
|
||||
* @date 2024/12/30 19:33
|
||||
*/
|
||||
public abstract class AbstractStorageDecorator<C> implements StorageStrategy<C> {
|
||||
|
||||
protected StorageStrategy<C> delegate;
|
||||
|
||||
protected AbstractStorageDecorator(StorageStrategy<C> delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public C getClient() {
|
||||
return delegate.getClient();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean bucketExists(String bucketName) {
|
||||
return delegate.bucketExists(bucketName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void createBucket(String bucketName) {
|
||||
delegate.createBucket(bucketName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UploadResp upload(String fileName, InputStream inputStream, String fileType) {
|
||||
return delegate.upload(fileName, inputStream, fileType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UploadResp upload(String fileName,
|
||||
String path,
|
||||
InputStream inputStream,
|
||||
String fileType,
|
||||
boolean isThumbnail) {
|
||||
return delegate.upload(fileName, path, inputStream, fileType, isThumbnail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UploadResp upload(String bucketName,
|
||||
String fileName,
|
||||
String path,
|
||||
InputStream inputStream,
|
||||
String fileType,
|
||||
boolean isThumbnail) {
|
||||
return delegate.upload(bucketName, fileName, path, inputStream, fileType, isThumbnail);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void upload(String bucketName, String fileName, String path, InputStream inputStream, String fileType) {
|
||||
delegate.upload(bucketName, fileName, path, inputStream, fileType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ThumbnailResp uploadThumbnail(String bucketName,
|
||||
String fileName,
|
||||
String path,
|
||||
InputStream inputStream,
|
||||
String fileType) {
|
||||
return delegate.uploadThumbnail(bucketName, fileName, path, inputStream, fileType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream download(String bucketName, String fileName) {
|
||||
return delegate.download(bucketName, fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delete(String bucketName, String fileName) {
|
||||
delegate.delete(bucketName, fileName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getImageBase64(String bucketName, String fileName) {
|
||||
return delegate.getImageBase64(bucketName, fileName);
|
||||
}
|
||||
}
|
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.storage.enums;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import top.continew.starter.core.enums.BaseEnum;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 文件类型枚举
|
||||
*
|
||||
* @author Charles7c
|
||||
* @since 2023/12/23 13:38
|
||||
*/
|
||||
public enum FileTypeEnum implements BaseEnum<Integer> {
|
||||
|
||||
/**
|
||||
* 其他
|
||||
*/
|
||||
UNKNOWN(1, "其他", Collections.emptyList()),
|
||||
|
||||
/**
|
||||
* 图片
|
||||
*/
|
||||
IMAGE(2, "图片", List
|
||||
.of("jpg", "jpeg", "png", "gif", "bmp", "webp", "ico", "psd", "tiff", "dwg", "jxr", "apng", "xcf")),
|
||||
|
||||
/**
|
||||
* 文档
|
||||
*/
|
||||
DOC(3, "文档", List.of("txt", "pdf", "doc", "xls", "ppt", "docx", "xlsx", "pptx")),
|
||||
|
||||
/**
|
||||
* 视频
|
||||
*/
|
||||
VIDEO(4, "视频", List.of("mp4", "avi", "mkv", "flv", "webm", "wmv", "m4v", "mov", "mpg", "rmvb", "3gp")),
|
||||
|
||||
/**
|
||||
* 音频
|
||||
*/
|
||||
AUDIO(5, "音频", List.of("mp3", "flac", "wav", "ogg", "midi", "m4a", "aac", "amr", "ac3", "aiff")),;
|
||||
|
||||
private final Integer value;
|
||||
private final String description;
|
||||
private final List<String> extensions;
|
||||
|
||||
/**
|
||||
* 根据扩展名查询
|
||||
*
|
||||
* @param extension 扩展名
|
||||
* @return 文件类型
|
||||
*/
|
||||
public static FileTypeEnum getByExtension(String extension) {
|
||||
return Arrays.stream(FileTypeEnum.values())
|
||||
.filter(t -> t.getExtensions().contains(StrUtil.emptyIfNull(extension).toLowerCase()))
|
||||
.findFirst()
|
||||
.orElse(FileTypeEnum.UNKNOWN);
|
||||
}
|
||||
|
||||
FileTypeEnum(Integer value, String description, List<String> extensions) {
|
||||
this.value = value;
|
||||
this.description = description;
|
||||
this.extensions = extensions;
|
||||
}
|
||||
|
||||
public List<String> getExtensions() {
|
||||
return this.extensions;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getValue() {
|
||||
return this.value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDescription() {
|
||||
return this.description;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getColor() {
|
||||
return BaseEnum.super.getColor();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.storage.manger;
|
||||
|
||||
import top.continew.starter.cache.redisson.util.RedisUtils;
|
||||
import top.continew.starter.core.validation.ValidationUtils;
|
||||
import top.continew.starter.storage.constant.StorageConstant;
|
||||
import top.continew.starter.storage.strategy.StorageStrategy;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* 存储策略管理器
|
||||
*
|
||||
* @author echo
|
||||
* @date 2024/12/16
|
||||
*/
|
||||
public class StorageManager {
|
||||
|
||||
/**
|
||||
* 存储策略连接信息
|
||||
*/
|
||||
private static final Map<String, StorageStrategy<?>> STORAGE_STRATEGY = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 加载存储策略
|
||||
*
|
||||
* @param code 存储码
|
||||
* @param strategy 对应存储策略
|
||||
*/
|
||||
public static void load(String code, StorageStrategy<?> strategy) {
|
||||
STORAGE_STRATEGY.put(code, strategy);
|
||||
}
|
||||
|
||||
/**
|
||||
* 卸载存储策略
|
||||
*
|
||||
* @param code 存储码
|
||||
*/
|
||||
public static void unload(String code) {
|
||||
STORAGE_STRATEGY.remove(code);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 存储 code 获取对应存储策略
|
||||
*
|
||||
* @param code 代码
|
||||
* @return {@link StorageStrategy }
|
||||
*/
|
||||
public static StorageStrategy<?> instance(String code) {
|
||||
StorageStrategy<?> strategy = STORAGE_STRATEGY.get(code);
|
||||
ValidationUtils.throwIfEmpty(strategy, "未找到存储配置:" + code);
|
||||
return strategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取默认存储策略
|
||||
*
|
||||
* @return {@link StorageStrategy }
|
||||
*/
|
||||
public static StorageStrategy<?> instance() {
|
||||
return instance(RedisUtils.get(StorageConstant.DEFAULT_KEY));
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.storage.model.req;
|
||||
|
||||
/**
|
||||
* 存储配置信息
|
||||
*
|
||||
* @author echo
|
||||
* @date 2024/11/04 15:13
|
||||
**/
|
||||
public class StorageProperties {
|
||||
|
||||
/**
|
||||
* 编码
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 访问密钥
|
||||
*/
|
||||
private String accessKey;
|
||||
|
||||
/**
|
||||
* 私有密钥
|
||||
*/
|
||||
private String secretKey;
|
||||
|
||||
/**
|
||||
* 终端节点
|
||||
*/
|
||||
private String endpoint;
|
||||
|
||||
/**
|
||||
* 桶名称
|
||||
*/
|
||||
private String bucketName;
|
||||
|
||||
/**
|
||||
* 域名
|
||||
*/
|
||||
private String domain;
|
||||
|
||||
/**
|
||||
* 作用域
|
||||
*/
|
||||
private String region;
|
||||
|
||||
/**
|
||||
* 是否是默认存储
|
||||
*/
|
||||
private Boolean isDefault;
|
||||
|
||||
public StorageProperties() {
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getAccessKey() {
|
||||
return accessKey;
|
||||
}
|
||||
|
||||
public void setAccessKey(String accessKey) {
|
||||
this.accessKey = accessKey;
|
||||
}
|
||||
|
||||
public String getSecretKey() {
|
||||
return secretKey;
|
||||
}
|
||||
|
||||
public void setSecretKey(String secretKey) {
|
||||
this.secretKey = secretKey;
|
||||
}
|
||||
|
||||
public String getEndpoint() {
|
||||
return endpoint;
|
||||
}
|
||||
|
||||
public void setEndpoint(String endpoint) {
|
||||
this.endpoint = endpoint;
|
||||
}
|
||||
|
||||
public String getBucketName() {
|
||||
return bucketName;
|
||||
}
|
||||
|
||||
public void setBucketName(String bucketName) {
|
||||
this.bucketName = bucketName;
|
||||
}
|
||||
|
||||
public String getDomain() {
|
||||
return domain;
|
||||
}
|
||||
|
||||
public void setDomain(String domain) {
|
||||
this.domain = domain;
|
||||
}
|
||||
|
||||
public String getRegion() {
|
||||
return region;
|
||||
}
|
||||
|
||||
public void setRegion(String region) {
|
||||
this.region = region;
|
||||
}
|
||||
|
||||
public Boolean getIsDefault() {
|
||||
return isDefault;
|
||||
}
|
||||
|
||||
public void setIsDefault(Boolean isDefault) {
|
||||
this.isDefault = isDefault;
|
||||
}
|
||||
|
||||
public StorageProperties(String code,
|
||||
String accessKey,
|
||||
String secretKey,
|
||||
String endpoint,
|
||||
String bucketName,
|
||||
String domain,
|
||||
String region,
|
||||
Boolean isDefault) {
|
||||
this.code = code;
|
||||
this.accessKey = accessKey;
|
||||
this.secretKey = secretKey;
|
||||
this.endpoint = endpoint;
|
||||
this.bucketName = bucketName;
|
||||
this.domain = domain;
|
||||
this.region = region;
|
||||
this.isDefault = isDefault;
|
||||
|
||||
}
|
||||
}
|
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.storage.model.resp;
|
||||
|
||||
/**
|
||||
* 缩略图
|
||||
*
|
||||
* @author echo
|
||||
* @date 2024/12/20 17:00
|
||||
*/
|
||||
public class ThumbnailResp {
|
||||
|
||||
/**
|
||||
* 缩略图大小(字节)
|
||||
*/
|
||||
private Long thumbnailSize;
|
||||
|
||||
/**
|
||||
* 缩略图地址 格式 xxx/xxx/xxx.small.jpg
|
||||
*/
|
||||
private String thumbnailPath;
|
||||
|
||||
public ThumbnailResp() {
|
||||
}
|
||||
|
||||
public ThumbnailResp(Long thumbnailSize, String thumbnailPath) {
|
||||
this.thumbnailSize = thumbnailSize;
|
||||
this.thumbnailPath = thumbnailPath;
|
||||
}
|
||||
|
||||
public Long getThumbnailSize() {
|
||||
return thumbnailSize;
|
||||
}
|
||||
|
||||
public void setThumbnailSize(Long thumbnailSize) {
|
||||
this.thumbnailSize = thumbnailSize;
|
||||
}
|
||||
|
||||
public String getThumbnailPath() {
|
||||
return thumbnailPath;
|
||||
}
|
||||
|
||||
public void setThumbnailPath(String thumbnailPath) {
|
||||
this.thumbnailPath = thumbnailPath;
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.storage.model.resp;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 上传结果
|
||||
*
|
||||
* @author echo
|
||||
* @date 2024/12/10
|
||||
*/
|
||||
public class UploadResp {
|
||||
|
||||
/**
|
||||
* 存储 code
|
||||
*/
|
||||
private String code;
|
||||
|
||||
/**
|
||||
* 访问地址
|
||||
* <p>如果桶为私有,则提供临时链接,时间默认为 12 小时</p>
|
||||
*/
|
||||
private String url;
|
||||
|
||||
/**
|
||||
* 文件基础路径
|
||||
*/
|
||||
private String basePath;
|
||||
|
||||
/**
|
||||
* 原始 文件名
|
||||
*/
|
||||
private String originalFilename;
|
||||
|
||||
/**
|
||||
* 扩展名
|
||||
*/
|
||||
private String ext;
|
||||
|
||||
/**
|
||||
* 文件大小(字节)
|
||||
*/
|
||||
private long size;
|
||||
|
||||
/**
|
||||
* 已上传对象的实体标记(用来校验文件)-S3
|
||||
*/
|
||||
private String eTag;
|
||||
|
||||
/**
|
||||
* 存储路径
|
||||
* <p></p> 格式 桶/文件名 continew/2024/12/24/1234.jpg
|
||||
*/
|
||||
private String path;
|
||||
|
||||
/**
|
||||
* 存储桶
|
||||
*/
|
||||
private String bucketName;
|
||||
|
||||
/**
|
||||
* 缩略图大小(字节)
|
||||
*/
|
||||
private Long thumbnailSize;
|
||||
|
||||
/**
|
||||
* 缩略图URL
|
||||
*/
|
||||
private String thumbnailUrl;
|
||||
|
||||
/**
|
||||
* 上传时间
|
||||
*/
|
||||
private LocalDateTime createTime;
|
||||
|
||||
public UploadResp() {
|
||||
}
|
||||
|
||||
public UploadResp(String code,
|
||||
String url,
|
||||
String basePath,
|
||||
String originalFilename,
|
||||
String ext,
|
||||
long size,
|
||||
String eTag,
|
||||
String path,
|
||||
String bucketName,
|
||||
Long thumbnailSize,
|
||||
String thumbnailUrl,
|
||||
LocalDateTime createTime) {
|
||||
this.code = code;
|
||||
this.url = url;
|
||||
this.basePath = basePath;
|
||||
this.originalFilename = originalFilename;
|
||||
this.ext = ext;
|
||||
this.size = size;
|
||||
this.eTag = eTag;
|
||||
this.path = path;
|
||||
this.bucketName = bucketName;
|
||||
this.thumbnailSize = thumbnailSize;
|
||||
this.thumbnailUrl = thumbnailUrl;
|
||||
this.createTime = createTime;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
return url;
|
||||
}
|
||||
|
||||
public void setUrl(String url) {
|
||||
this.url = url;
|
||||
}
|
||||
|
||||
public String getBasePath() {
|
||||
return basePath;
|
||||
}
|
||||
|
||||
public void setBasePath(String basePath) {
|
||||
this.basePath = basePath;
|
||||
}
|
||||
|
||||
public String getOriginalFilename() {
|
||||
return originalFilename;
|
||||
}
|
||||
|
||||
public void setOriginalFilename(String originalFilename) {
|
||||
this.originalFilename = originalFilename;
|
||||
}
|
||||
|
||||
public String getExt() {
|
||||
return ext;
|
||||
}
|
||||
|
||||
public void setExt(String ext) {
|
||||
this.ext = ext;
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size;
|
||||
}
|
||||
|
||||
public void setSize(long size) {
|
||||
this.size = size;
|
||||
}
|
||||
|
||||
public String geteTag() {
|
||||
return eTag;
|
||||
}
|
||||
|
||||
public void seteTag(String eTag) {
|
||||
this.eTag = eTag;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
|
||||
public void setPath(String path) {
|
||||
this.path = path;
|
||||
}
|
||||
|
||||
public String getBucketName() {
|
||||
return bucketName;
|
||||
}
|
||||
|
||||
public void setBucketName(String bucketName) {
|
||||
this.bucketName = bucketName;
|
||||
}
|
||||
|
||||
public Long getThumbnailSize() {
|
||||
return thumbnailSize;
|
||||
}
|
||||
|
||||
public void setThumbnailSize(Long thumbnailSize) {
|
||||
this.thumbnailSize = thumbnailSize;
|
||||
}
|
||||
|
||||
public String getThumbnailUrl() {
|
||||
return thumbnailUrl;
|
||||
}
|
||||
|
||||
public void setThumbnailUrl(String thumbnailUrl) {
|
||||
this.thumbnailUrl = thumbnailUrl;
|
||||
}
|
||||
|
||||
public LocalDateTime getCreateTime() {
|
||||
return createTime;
|
||||
}
|
||||
|
||||
public void setCreateTime(LocalDateTime createTime) {
|
||||
this.createTime = createTime;
|
||||
}
|
||||
}
|
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.storage.strategy;
|
||||
|
||||
import top.continew.starter.storage.model.resp.ThumbnailResp;
|
||||
import top.continew.starter.storage.model.resp.UploadResp;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* 存储策略接口
|
||||
*
|
||||
* @author echo
|
||||
* @date 2024/12/16 11:19
|
||||
*/
|
||||
public interface StorageStrategy<C> {
|
||||
|
||||
/**
|
||||
* 获得客户端 - 用于重写时 获取对应存储 code 客户端
|
||||
*
|
||||
* @return {@link Object }
|
||||
*/
|
||||
C getClient();
|
||||
|
||||
/**
|
||||
* 检查桶是否存在
|
||||
* <p> S3: 检查桶是否存在 </p>
|
||||
* <p>local: 检查 默认路径 是否存在 </p>
|
||||
*
|
||||
* @param bucketName 桶名称
|
||||
* @return true 存在 false 不存在
|
||||
*/
|
||||
boolean bucketExists(String bucketName);
|
||||
|
||||
/**
|
||||
* 创建桶
|
||||
* <p> S3: 创建桶 </p>
|
||||
* <p> local: 创建 默认路径下 指定文件夹 </p>
|
||||
*
|
||||
* @param bucketName 桶名称
|
||||
*/
|
||||
void createBucket(String bucketName);
|
||||
|
||||
/**
|
||||
* 上传文件 - 默认桶
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @param inputStream 输入流
|
||||
* @param fileType 文件类型
|
||||
* @return 上传响应
|
||||
*/
|
||||
UploadResp upload(String fileName, InputStream inputStream, String fileType);
|
||||
|
||||
/**
|
||||
* 上传文件 - 默认桶
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @param path 路径
|
||||
* @param inputStream 输入流
|
||||
* @param fileType 文件类型
|
||||
* @param isThumbnail 是缩略图
|
||||
* @return {@link UploadResp }
|
||||
*/
|
||||
UploadResp upload(String fileName, String path, InputStream inputStream, String fileType, boolean isThumbnail);
|
||||
|
||||
/**
|
||||
* 上传文件
|
||||
*
|
||||
* @param bucketName 桶名称
|
||||
* @param fileName 文件名
|
||||
* @param path 路径
|
||||
* @param inputStream 输入流
|
||||
* @param fileType 文件类型
|
||||
* @param isThumbnail 是缩略图
|
||||
* @return 上传响应
|
||||
*/
|
||||
UploadResp upload(String bucketName,
|
||||
String fileName,
|
||||
String path,
|
||||
InputStream inputStream,
|
||||
String fileType,
|
||||
boolean isThumbnail);
|
||||
|
||||
/**
|
||||
* 文件上传-基础上传
|
||||
*
|
||||
* @param bucketName 桶名称 - 基础上传不做处理
|
||||
* @param fileName 文件名 - 基础上传不做处理
|
||||
* @param path 路径 - 基础上传不做处理
|
||||
* @param inputStream 输入流
|
||||
* @param fileType 文件类型
|
||||
* @return {@link UploadResp }
|
||||
*/
|
||||
void upload(String bucketName, String fileName, String path, InputStream inputStream, String fileType);
|
||||
|
||||
/**
|
||||
* 上传缩略图
|
||||
*
|
||||
* @param bucketName 桶名称
|
||||
* @param fileName 文件名
|
||||
* @param inputStream 输入流
|
||||
* @param fileType 文件类型
|
||||
* @return {@link UploadResp }
|
||||
*/
|
||||
ThumbnailResp uploadThumbnail(String bucketName,
|
||||
String fileName,
|
||||
String path,
|
||||
InputStream inputStream,
|
||||
String fileType);
|
||||
|
||||
/**
|
||||
* 下载文件
|
||||
*
|
||||
* @param bucketName 桶名称
|
||||
* @param fileName 文件名
|
||||
* @return 文件输入流
|
||||
*/
|
||||
InputStream download(String bucketName, String fileName);
|
||||
|
||||
/**
|
||||
* 删除文件
|
||||
*
|
||||
* @param bucketName 桶名称
|
||||
* @param fileName 文件名
|
||||
*/
|
||||
void delete(String bucketName, String fileName);
|
||||
|
||||
/**
|
||||
* 获取图像Base64
|
||||
*
|
||||
* @param bucketName 桶名称
|
||||
* @param fileName 文件名
|
||||
* @return Base64编码的图像
|
||||
*/
|
||||
String getImageBase64(String bucketName, String fileName);
|
||||
|
||||
}
|
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.storage.util;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import java.awt.*;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
* 图像缩略图工具
|
||||
*
|
||||
* @author echo
|
||||
* @date 2024/12/20 16:49
|
||||
*/
|
||||
public class ImageThumbnailUtils {
|
||||
|
||||
// 默认缩略图尺寸:100x100
|
||||
private static final int DEFAULT_WIDTH = 100;
|
||||
private static final int DEFAULT_HEIGHT = 100;
|
||||
|
||||
/**
|
||||
* 根据输入流生成默认大小(100x100)的缩略图并写入输出流
|
||||
*
|
||||
* @param inputStream 原始图片的输入流
|
||||
* @param outputStream 缩略图输出流
|
||||
* @param suffix 后缀
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
public static void generateThumbnail(InputStream inputStream,
|
||||
OutputStream outputStream,
|
||||
String suffix) throws IOException {
|
||||
generateThumbnail(inputStream, outputStream, DEFAULT_WIDTH, DEFAULT_HEIGHT, suffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据输入流和自定义尺寸生成缩略图并写入输出流
|
||||
*
|
||||
* @param inputStream 原始图片的输入流
|
||||
* @param outputStream 缩略图输出流
|
||||
* @param width 缩略图宽度
|
||||
* @param height 缩略图高度
|
||||
* @param suffix 后缀
|
||||
* @throws IOException IOException
|
||||
*/
|
||||
public static void generateThumbnail(InputStream inputStream,
|
||||
OutputStream outputStream,
|
||||
int width,
|
||||
int height,
|
||||
String suffix) throws IOException {
|
||||
// 读取原始图片
|
||||
BufferedImage originalImage = ImageIO.read(inputStream);
|
||||
|
||||
// 调整图片大小
|
||||
Image tmp = originalImage.getScaledInstance(width, height, Image.SCALE_SMOOTH);
|
||||
BufferedImage thumbnail = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
|
||||
|
||||
// 画出缩略图
|
||||
Graphics2D g2d = thumbnail.createGraphics();
|
||||
g2d.drawImage(tmp, 0, 0, null);
|
||||
g2d.dispose();
|
||||
// 写入输出流
|
||||
ImageIO.write(thumbnail, suffix, outputStream);
|
||||
}
|
||||
}
|
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
* <p>
|
||||
* Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
* <p>
|
||||
* http://www.gnu.org/licenses/lgpl.html
|
||||
* <p>
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package top.continew.starter.storage.util;
|
||||
|
||||
import cn.hutool.core.io.FileUtil;
|
||||
import cn.hutool.core.io.IoUtil;
|
||||
import cn.hutool.core.io.file.FileNameUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import top.continew.starter.core.constant.StringConstants;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.nio.file.Paths;
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
|
||||
/**
|
||||
* 储存工具
|
||||
*
|
||||
* @author echo
|
||||
* @date 2024/12/16 19:55
|
||||
*/
|
||||
public class StorageUtils {
|
||||
public StorageUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式文件名
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return {@link String }
|
||||
*/
|
||||
public static String formatFileName(String fileName) {
|
||||
// 获取文件后缀名
|
||||
String suffix = FileUtil.extName(fileName);
|
||||
// 获取当前时间的年月日时分秒格式
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
|
||||
String datetime = LocalDateTime.now().format(formatter);
|
||||
// 获取当前时间戳
|
||||
String timestamp = String.valueOf(System.currentTimeMillis());
|
||||
// 生成新的文件名
|
||||
return datetime + timestamp + "." + suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认文件目录
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @return {@link String }
|
||||
*/
|
||||
public static String defaultFileDir(String fileName) {
|
||||
LocalDate today = LocalDate.now();
|
||||
return Paths.get(String.valueOf(today.getYear()), String.valueOf(today.getMonthValue()), String.valueOf(today
|
||||
.getDayOfMonth()), fileName).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* 默认路径地址 格式 2024/03/10/
|
||||
*
|
||||
* @return {@link String }
|
||||
*/
|
||||
public static String defaultPath() {
|
||||
LocalDate today = LocalDate.now();
|
||||
return Paths.get(String.valueOf(today.getYear()), String.valueOf(today.getMonthValue()), String.valueOf(today
|
||||
.getDayOfMonth())) + StringConstants.SLASH;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据 endpoint 判断是否带有 http 或 https,如果没有则加上 http 前缀。
|
||||
*
|
||||
* @param endpoint 输入的 endpoint 字符串
|
||||
* @return URI 对象
|
||||
*/
|
||||
public static URI createUriWithProtocol(String endpoint) {
|
||||
// 判断 endpoint 是否包含 http:// 或 https:// 前缀
|
||||
if (!endpoint.startsWith("http://") && !endpoint.startsWith("https://")) {
|
||||
// 如果没有协议前缀,则加上 http://
|
||||
endpoint = "http://" + endpoint;
|
||||
}
|
||||
// 返回 URI 对象
|
||||
return URI.create(endpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成缩略图文件名
|
||||
*
|
||||
* @param fileName 文件名
|
||||
* @param suffix 后缀
|
||||
* @return {@link String }
|
||||
*/
|
||||
public static String buildThumbnailFileName(String fileName, String suffix) {
|
||||
// 获取文件的扩展名
|
||||
String extName = FileNameUtil.extName(fileName);
|
||||
// 去掉扩展名
|
||||
String baseName = StrUtil.subBefore(fileName, StringConstants.DOT, true);
|
||||
// 拼接新的路径:原始路径 + .缩略图后缀 + .扩展名
|
||||
return baseName + "." + suffix + "." + extName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 可重复读流
|
||||
*
|
||||
* @param inputStream 输入流
|
||||
* @return {@link InputStream }
|
||||
*/
|
||||
public static InputStream ensureByteArrayStream(InputStream inputStream) {
|
||||
return (inputStream instanceof ByteArrayInputStream)
|
||||
? inputStream
|
||||
: new ByteArrayInputStream(IoUtil.readBytes(inputStream));
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user