From 37027c774b2f65b7feec63657b2befda6a3896ae Mon Sep 17 00:00:00 2001
From: Charles7c
Date: Thu, 15 May 2025 23:25:35 +0800
Subject: [PATCH] =?UTF-8?q?refactor(system/file):=20=E9=87=8D=E6=9E=84?=
=?UTF-8?q?=E6=96=87=E4=BB=B6=E7=AE=A1=E7=90=86=E7=9B=B8=E5=85=B3=E4=BB=A3?=
=?UTF-8?q?=E7=A0=81=EF=BC=8C=E5=AE=8C=E5=96=84=E6=96=87=E4=BB=B6=E5=A4=B9?=
=?UTF-8?q?=E5=9C=BA=E6=99=AF?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../system/config/file/FileRecorderImpl.java | 49 ++++++--
.../config/file/FileStorageConfigLoader.java | 11 +-
.../admin/system/enums/StorageTypeEnum.java | 15 ++-
.../admin/system/model/entity/FileDO.java | 69 +++++------
.../admin/system/model/query/FileQuery.java | 11 +-
.../admin/system/model/req/FileReq.java | 10 +-
.../system/model/resp/file/FileResp.java | 42 ++++---
.../admin/system/service/FileService.java | 3 +-
.../system/service/impl/FileServiceImpl.java | 108 +++++++++++++-----
.../service/impl/StorageServiceImpl.java | 7 +-
.../controller/system/FileController.java | 5 +-
.../db/changelog/mysql/main_data.sql | 4 +-
.../db/changelog/mysql/main_table.sql | 20 ++--
.../db/changelog/postgresql/main_data.sql | 4 +-
.../db/changelog/postgresql/main_table.sql | 21 ++--
15 files changed, 222 insertions(+), 157 deletions(-)
diff --git a/continew-module-system/src/main/java/top/continew/admin/system/config/file/FileRecorderImpl.java b/continew-module-system/src/main/java/top/continew/admin/system/config/file/FileRecorderImpl.java
index 4dc429be..ea0ac406 100644
--- a/continew-module-system/src/main/java/top/continew/admin/system/config/file/FileRecorderImpl.java
+++ b/continew-module-system/src/main/java/top/continew/admin/system/config/file/FileRecorderImpl.java
@@ -16,9 +16,11 @@
package top.continew.admin.system.config.file;
+import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
+import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.x.file.storage.core.FileInfo;
@@ -32,7 +34,9 @@ import top.continew.admin.system.model.entity.StorageDO;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.util.URLUtils;
-import java.util.Optional;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
/**
* 文件记录实现类
@@ -58,11 +62,11 @@ public class FileRecorderImpl implements FileRecorder {
// 方便文件上传完成后获取文件信息
fileInfo.setId(String.valueOf(file.getId()));
if (!URLUtils.isHttpUrl(fileInfo.getUrl())) {
- String prefix = StrUtil.appendIfMissing(storage.getDomain(), StringConstants.SLASH);
- String url = URLUtil.normalize(prefix + fileInfo.getUrl());
+ String prefix = StrUtil.blankToDefault(storage.getDomain(), storage.getEndpoint());
+ String url = URLUtil.completeUrl(prefix, fileInfo.getUrl());
fileInfo.setUrl(url);
if (StrUtil.isNotBlank(fileInfo.getThUrl())) {
- fileInfo.setThUrl(URLUtil.normalize(prefix + fileInfo.getThUrl()));
+ fileInfo.setThUrl(URLUtil.completeUrl(prefix, fileInfo.getThUrl()));
}
}
return true;
@@ -81,7 +85,10 @@ public class FileRecorderImpl implements FileRecorder {
@Override
public boolean delete(String url) {
FileDO file = this.getFileByUrl(url);
- return fileMapper.lambdaUpdate().eq(FileDO::getUrl, file.getUrl()).remove();
+ if (null == file) {
+ return false;
+ }
+ return fileMapper.lambdaUpdate().eq(FileDO::getId, file.getId()).remove();
}
@Override
@@ -106,10 +113,32 @@ public class FileRecorderImpl implements FileRecorder {
* @return 文件信息
*/
private FileDO getFileByUrl(String url) {
- Optional fileOptional = fileMapper.lambdaQuery().eq(FileDO::getUrl, url).oneOpt();
- return fileOptional.orElseGet(() -> fileMapper.lambdaQuery()
- .likeLeft(FileDO::getUrl, StrUtil.subAfter(url, StringConstants.SLASH, true))
- .oneOpt()
- .orElse(null));
+ LambdaQueryChainWrapper queryWrapper = fileMapper.lambdaQuery()
+ .eq(FileDO::getName, StrUtil.subAfter(url, StringConstants.SLASH, true));
+ // 非 HTTP URL 场景
+ if (!URLUtils.isHttpUrl(url)) {
+ return queryWrapper.eq(FileDO::getPath, StrUtil.prependIfMissing(StrUtil
+ .subBefore(url, StringConstants.SLASH, true), StringConstants.SLASH)).one();
+ }
+ // HTTP URL 场景
+ List list = queryWrapper.list();
+ if (CollUtil.isEmpty(list)) {
+ return null;
+ }
+ if (list.size() == 1) {
+ return list.get(0);
+ }
+ // 结合存储配置进行匹配
+ List storageList = storageMapper.selectByIds(list.stream().map(FileDO::getStorageId).toList());
+ Map storageMap = storageList.stream()
+ .collect(Collectors.toMap(StorageDO::getId, storage -> storage));
+ return list.stream().filter(file -> {
+ // http://localhost:8000/file/user/avatar/6825e687db4174e7a297a5f8.png => http://localhost:8000/file/user/avatar
+ String urlPrefix = StrUtil.subBefore(url, StringConstants.SLASH, true);
+ // http://localhost:8000/file/ + /user/avatar => http://localhost:8000/file/user/avatar
+ StorageDO storage = storageMap.get(file.getStorageId());
+ String prefix = StrUtil.blankToDefault(storage.getDomain(), storage.getEndpoint());
+ return urlPrefix.equals(URLUtil.normalize(prefix + file.getPath(), false, true));
+ }).findFirst().orElse(null);
}
}
\ No newline at end of file
diff --git a/continew-module-system/src/main/java/top/continew/admin/system/config/file/FileStorageConfigLoader.java b/continew-module-system/src/main/java/top/continew/admin/system/config/file/FileStorageConfigLoader.java
index 8e3e26b0..fecf2503 100644
--- a/continew-module-system/src/main/java/top/continew/admin/system/config/file/FileStorageConfigLoader.java
+++ b/continew-module-system/src/main/java/top/continew/admin/system/config/file/FileStorageConfigLoader.java
@@ -16,7 +16,6 @@
package top.continew.admin.system.config.file;
-import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -25,8 +24,6 @@ import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import top.continew.admin.common.enums.DisEnableStatusEnum;
import top.continew.admin.system.model.entity.StorageDO;
-import top.continew.admin.system.model.query.StorageQuery;
-import top.continew.admin.system.model.resp.StorageResp;
import top.continew.admin.system.service.StorageService;
import java.util.List;
@@ -46,12 +43,10 @@ public class FileStorageConfigLoader implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) {
- StorageQuery query = new StorageQuery();
- query.setStatus(DisEnableStatusEnum.ENABLE);
- List storageList = storageService.list(query, null);
- if (CollUtil.isEmpty(storageList)) {
+ List list = storageService.lambdaQuery().eq(StorageDO::getStatus, DisEnableStatusEnum.ENABLE).list();
+ if (CollUtil.isEmpty(list)) {
return;
}
- storageList.forEach(storage -> storageService.load(BeanUtil.copyProperties(storage, StorageDO.class)));
+ list.forEach(storageService::load);
}
}
diff --git a/continew-module-system/src/main/java/top/continew/admin/system/enums/StorageTypeEnum.java b/continew-module-system/src/main/java/top/continew/admin/system/enums/StorageTypeEnum.java
index 54981411..beaf0bb2 100644
--- a/continew-module-system/src/main/java/top/continew/admin/system/enums/StorageTypeEnum.java
+++ b/continew-module-system/src/main/java/top/continew/admin/system/enums/StorageTypeEnum.java
@@ -43,14 +43,15 @@ public enum StorageTypeEnum implements BaseEnum {
@Override
public void validate(StorageReq req) {
ValidationUtils.validate(req, ValidationGroup.Storage.Local.class);
- ValidationUtils.throwIf(!URLUtils.isHttpUrl(req.getDomain()), "访问路径格式不正确");
+ ValidationUtils.throwIf(StrUtil.isNotBlank(req.getDomain()) && !URLUtils.isHttpUrl(req
+ .getDomain()), "访问路径格式不正确");
}
@Override
public void pretreatment(StorageReq req) {
super.pretreatment(req);
- req.setBucketName(StrUtil.appendIfMissing(req.getBucketName()
- .replace(StringConstants.BACKSLASH, StringConstants.SLASH), StringConstants.SLASH));
+ // 本地存储路径需要以 “/” 结尾
+ req.setBucketName(StrUtil.appendIfMissing(req.getBucketName(), StringConstants.SLASH));
}
},
@@ -61,7 +62,8 @@ public enum StorageTypeEnum implements BaseEnum {
@Override
public void validate(StorageReq req) {
ValidationUtils.validate(req, ValidationGroup.Storage.OSS.class);
- ValidationUtils.throwIf(!URLUtils.isHttpUrl(req.getDomain()), "域名格式不正确");
+ ValidationUtils.throwIf(StrUtil.isNotBlank(req.getDomain()) && !URLUtils.isHttpUrl(req
+ .getDomain()), "域名格式不正确");
}
};
@@ -81,6 +83,9 @@ public enum StorageTypeEnum implements BaseEnum {
* @param req 请求参数
*/
public void pretreatment(StorageReq req) {
- req.setDomain(StrUtil.removeSuffix(req.getDomain(), StringConstants.SLASH));
+ // 域名需要以 “/” 结尾
+ if (StrUtil.isNotBlank(req.getDomain())) {
+ req.setDomain(StrUtil.appendIfMissing(req.getDomain(), StringConstants.SLASH));
+ }
}
}
diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/FileDO.java b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/FileDO.java
index 6578b8d9..4ceaef57 100644
--- a/continew-module-system/src/main/java/top/continew/admin/system/model/entity/FileDO.java
+++ b/continew-module-system/src/main/java/top/continew/admin/system/model/entity/FileDO.java
@@ -17,8 +17,6 @@
package top.continew.admin.system.model.entity;
import cn.hutool.core.date.DateUtil;
-import cn.hutool.core.io.file.FileNameUtil;
-import cn.hutool.core.util.EscapeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.annotation.TableName;
@@ -28,7 +26,6 @@ import org.dromara.x.file.storage.core.FileInfo;
import top.continew.admin.common.model.entity.BaseDO;
import top.continew.admin.system.enums.FileTypeEnum;
import top.continew.starter.core.constant.StringConstants;
-import top.continew.starter.core.util.StrUtils;
import java.io.Serial;
import java.util.Map;
@@ -52,25 +49,20 @@ public class FileDO extends BaseDO {
*/
private String name;
+ /**
+ * 原始名称
+ */
+ private String originalName;
+
/**
* 大小(字节)
*/
private Long size;
/**
- * URL
+ * 存储路径
*/
- private String url;
-
- /**
- * 上级目录
- */
- private String parentPath;
-
- /**
- * 绝对路径
- */
- private String absPath;
+ private String path;
/**
* 扩展名
@@ -88,7 +80,7 @@ public class FileDO extends BaseDO {
private FileTypeEnum type;
/**
- * SHA256值
+ * SHA256 值
*/
private String sha256;
@@ -97,16 +89,16 @@ public class FileDO extends BaseDO {
*/
private String metadata;
+ /**
+ * 缩略图名称
+ */
+ private String thumbnailName;
+
/**
* 缩略图大小(字节)
*/
private Long thumbnailSize;
- /**
- * 缩略图 URL
- */
- private String thumbnailUrl;
-
/**
* 缩略图元数据
*/
@@ -123,21 +115,21 @@ public class FileDO extends BaseDO {
* @param fileInfo {@link FileInfo} 文件信息
*/
public FileDO(FileInfo fileInfo) {
- this.name = FileNameUtil.getPrefix(EscapeUtil.unescape(fileInfo.getOriginalFilename()));
+ this.name = fileInfo.getFilename();
+ this.originalName = fileInfo.getOriginalFilename();
this.size = fileInfo.getSize();
- this.url = fileInfo.getUrl();
- this.absPath = StrUtil.isEmpty(fileInfo.getPath())
+ // 如果为空,则为 /;如果不为空,则调整格式为:/xxx
+ this.path = StrUtil.isEmpty(fileInfo.getPath())
? StringConstants.SLASH
- : StrUtil.prependIfMissing(fileInfo.getPath(), StringConstants.SLASH);
- String[] pathAttr = this.absPath.split(StringConstants.SLASH);
- this.parentPath = pathAttr.length > 1 ? pathAttr[pathAttr.length - 1] : StringConstants.SLASH;
+ : StrUtil.removeSuffix(StrUtil.prependIfMissing(fileInfo
+ .getPath(), StringConstants.SLASH), StringConstants.SLASH);
this.extension = fileInfo.getExt();
this.contentType = fileInfo.getContentType();
this.type = FileTypeEnum.getByExtension(this.extension);
this.sha256 = fileInfo.getHashInfo().getSha256();
this.metadata = JSONUtil.toJsonStr(fileInfo.getMetadata());
+ this.thumbnailName = fileInfo.getThFilename();
this.thumbnailSize = fileInfo.getThSize();
- this.thumbnailUrl = fileInfo.getThUrl();
this.thumbnailMetadata = JSONUtil.toJsonStr(fileInfo.getThMetadata());
this.setCreateTime(DateUtil.toLocalDateTime(fileInfo.getCreateTime()));
}
@@ -151,27 +143,24 @@ public class FileDO extends BaseDO {
public FileInfo toFileInfo(StorageDO storage) {
FileInfo fileInfo = new FileInfo();
fileInfo.setPlatform(storage.getCode());
- fileInfo.setFilename(StrUtil.contains(this.url, StringConstants.SLASH)
- ? StrUtil.subAfter(this.url, StringConstants.SLASH, true)
- : this.url);
- fileInfo.setOriginalFilename(StrUtils
- .blankToDefault(this.extension, this.name, ex -> this.name + StringConstants.DOT + ex));
+ fileInfo.setFilename(this.name);
+ fileInfo.setOriginalFilename(this.originalName);
+ // 暂不使用,所以保持空
fileInfo.setBasePath(StringConstants.EMPTY);
fileInfo.setSize(this.size);
- fileInfo.setUrl(this.url);
- fileInfo.setPath(StringConstants.SLASH.equals(this.absPath)
+ fileInfo.setPath(StringConstants.SLASH.equals(this.path)
? StringConstants.EMPTY
- : StrUtil.removePrefix(this.absPath, StringConstants.SLASH));
+ : StrUtil.appendIfMissing(StrUtil.removePrefix(this.path, StringConstants.SLASH), StringConstants.SLASH));
fileInfo.setExt(this.extension);
+ fileInfo.setContentType(this.contentType);
if (StrUtil.isNotBlank(this.metadata)) {
fileInfo.setMetadata(JSONUtil.toBean(this.metadata, Map.class));
}
+ fileInfo.setUrl(fileInfo.getPath() + fileInfo.getFilename());
// 缩略图信息
- fileInfo.setThFilename(StrUtil.contains(this.thumbnailUrl, StringConstants.SLASH)
- ? StrUtil.subAfter(this.thumbnailUrl, StringConstants.SLASH, true)
- : this.thumbnailUrl);
+ fileInfo.setThFilename(this.thumbnailName);
fileInfo.setThSize(this.thumbnailSize);
- fileInfo.setThUrl(this.thumbnailUrl);
+ fileInfo.setThUrl(fileInfo.getPath() + fileInfo.getThFilename());
if (StrUtil.isNotBlank(this.thumbnailMetadata)) {
fileInfo.setThMetadata(JSONUtil.toBean(this.thumbnailMetadata, Map.class));
}
diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/query/FileQuery.java b/continew-module-system/src/main/java/top/continew/admin/system/model/query/FileQuery.java
index 6018229e..39949c95 100644
--- a/continew-module-system/src/main/java/top/continew/admin/system/model/query/FileQuery.java
+++ b/continew-module-system/src/main/java/top/continew/admin/system/model/query/FileQuery.java
@@ -41,16 +41,15 @@ public class FileQuery implements Serializable {
/**
* 名称
*/
- @Schema(description = "名称", example = "图片")
+ @Schema(description = "名称", example = "example")
@Query(type = QueryType.LIKE)
- private String name;
+ private String originalName;
/**
- * 绝对路径
+ * 存储路径
*/
- @Schema(description = "绝对路径", example = "/2025")
- @Query(type = QueryType.EQ)
- private String absPath;
+ @Schema(description = "存储路径", example = "/")
+ private String path;
/**
* 类型
diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/req/FileReq.java b/continew-module-system/src/main/java/top/continew/admin/system/model/req/FileReq.java
index a886c3bc..118c2d71 100644
--- a/continew-module-system/src/main/java/top/continew/admin/system/model/req/FileReq.java
+++ b/continew-module-system/src/main/java/top/continew/admin/system/model/req/FileReq.java
@@ -40,14 +40,14 @@ public class FileReq implements Serializable {
/**
* 名称
*/
- @Schema(description = "名称", example = "test123")
+ @Schema(description = "名称", example = "example")
@NotBlank(message = "文件名称不能为空")
@Length(max = 255, message = "文件名称长度不能超过 {max} 个字符")
- private String name;
+ private String originalName;
/**
- * 上级目录
+ * 存储路径
*/
- @Schema(description = "上级目录", example = "25")
- private String parentPath;
+ @Schema(description = "存储路径", example = "/")
+ private String path;
}
\ No newline at end of file
diff --git a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/file/FileResp.java b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/file/FileResp.java
index dea51907..502a431a 100644
--- a/continew-module-system/src/main/java/top/continew/admin/system/model/resp/file/FileResp.java
+++ b/continew-module-system/src/main/java/top/continew/admin/system/model/resp/file/FileResp.java
@@ -41,9 +41,15 @@ public class FileResp extends BaseDetailResp {
/**
* 名称
*/
- @Schema(description = "名称", example = "example")
+ @Schema(description = "名称", example = "6824afe8408da079832dcfb6.jpg")
private String name;
+ /**
+ * 原始名称
+ */
+ @Schema(description = "原始名称", example = "example.jpg")
+ private String originalName;
+
/**
* 大小(字节)
*/
@@ -53,20 +59,14 @@ public class FileResp extends BaseDetailResp {
/**
* URL
*/
- @Schema(description = "URL", example = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/example/example.jpg")
+ @Schema(description = "URL", example = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/2025/2/25/6824afe8408da079832dcfb6.jpg")
private String url;
/**
- * 上级目录
+ * 存储路径
*/
- @Schema(description = "上级目录", example = "25")
- private String parentPath;
-
- /**
- * 绝对路径
- */
- @Schema(description = "绝对路径", example = "/2025/2/25")
- private String absPath;
+ @Schema(description = "上级目录", example = "/2025/2/25")
+ private String path;
/**
* 扩展名
@@ -89,7 +89,7 @@ public class FileResp extends BaseDetailResp {
/**
* SHA256 值
*/
- @Schema(description = "SHA256值", example = "722f185c48bed892d6fa12e2b8bf1e5f8200d4a70f522fb62112b6caf13cb74e")
+ @Schema(description = "SHA256 值", example = "722f185c48bed892d6fa12e2b8bf1e5f8200d4a70f522fb62112b6caf13cb74e")
private String sha256;
/**
@@ -98,24 +98,30 @@ public class FileResp extends BaseDetailResp {
@Schema(description = "元数据", example = "{width:1024,height:1024}")
private String metadata;
+ /**
+ * 缩略图名称
+ */
+ @Schema(description = "缩略图名称", example = "example.jpg.min.jpg")
+ private String thumbnailName;
+
/**
* 缩略图大小(字节)
*/
@Schema(description = "缩略图大小(字节)", example = "1024")
private Long thumbnailSize;
- /**
- * 缩略图 URL
- */
- @Schema(description = "缩略图 URL", example = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/example/example.jpg.min.jpg")
- private String thumbnailUrl;
-
/**
* 缩略图元数据
*/
@Schema(description = "缩略图文件元数据", example = "{width:100,height:100}")
private String thumbnailMetadata;
+ /**
+ * 缩略图 URL
+ */
+ @Schema(description = "缩略图 URL", example = "https://examplebucket.oss-cn-hangzhou.aliyuncs.com/2025/2/25/example.jpg.min.jpg")
+ private String thumbnailUrl;
+
/**
* 存储 ID
*/
diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/FileService.java b/continew-module-system/src/main/java/top/continew/admin/system/service/FileService.java
index 192141b1..a90ba53a 100644
--- a/continew-module-system/src/main/java/top/continew/admin/system/service/FileService.java
+++ b/continew-module-system/src/main/java/top/continew/admin/system/service/FileService.java
@@ -25,7 +25,6 @@ import top.continew.admin.system.model.resp.file.FileResp;
import top.continew.admin.system.model.resp.file.FileStatisticsResp;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.data.mp.service.IService;
-import top.continew.starter.extension.crud.model.resp.IdResp;
import top.continew.starter.extension.crud.service.BaseService;
import java.io.IOException;
@@ -103,7 +102,7 @@ public interface FileService extends BaseService createDir(FileReq req);
+ Long createDir(FileReq req);
/**
* 获取默认文件路径
diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/FileServiceImpl.java b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/FileServiceImpl.java
index 4b47e86e..23bf13c1 100644
--- a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/FileServiceImpl.java
+++ b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/FileServiceImpl.java
@@ -18,6 +18,7 @@ package top.continew.admin.system.service.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.file.FileNameUtil;
+import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ClassUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
@@ -43,12 +44,11 @@ import top.continew.admin.system.service.FileService;
import top.continew.admin.system.service.StorageService;
import top.continew.starter.core.constant.StringConstants;
import top.continew.starter.core.util.StrUtils;
-import top.continew.starter.core.util.URLUtils;
import top.continew.starter.core.validation.CheckUtils;
-import top.continew.starter.extension.crud.model.resp.IdResp;
import top.continew.starter.extension.crud.service.BaseServiceImpl;
import java.io.IOException;
+import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
@@ -75,8 +75,19 @@ public class FileServiceImpl extends BaseServiceImpl> entry : fileListGroup.entrySet()) {
StorageDO storage = storageService.getById(entry.getKey());
for (FileDO file : entry.getValue()) {
- FileInfo fileInfo = file.toFileInfo(storage);
- fileStorageService.delete(fileInfo);
+ if (!FileTypeEnum.DIR.equals(file.getType())) {
+ FileInfo fileInfo = file.toFileInfo(storage);
+ fileStorageService.delete(fileInfo);
+ } else {
+ // 不允许删除非空文件夹
+ String separator = StringConstants.SLASH.equals(file.getPath())
+ ? StringConstants.EMPTY
+ : StringConstants.SLASH;
+ boolean exists = baseMapper.lambdaQuery()
+ .eq(FileDO::getPath, file.getPath() + separator + file.getName())
+ .exists();
+ CheckUtils.throwIf(exists, "文件夹 [{}] 不为空,请先删除文件夹下的内容", file.getName());
+ }
}
}
}
@@ -117,6 +128,8 @@ public class FileServiceImpl extends BaseServiceImpl createDir(FileReq req) {
+ public Long createDir(FileReq req) {
StorageDO storage = storageService.getDefaultStorage();
- FileDO fileDo = new FileDO();
- fileDo.setName(req.getName());
- fileDo.setSize(0L);
- fileDo.setUrl(storage.getDomain() + req.getParentPath() + req.getName());
- String absPath = req.getParentPath();
- String tempAbsPath = absPath.length() > 1 ? StrUtil.removeSuffix(absPath, StringConstants.SLASH) : absPath;
- String[] pathArr = tempAbsPath.split(StringConstants.SLASH);
- if (pathArr.length > 1) {
- fileDo.setParentPath(pathArr[pathArr.length - 1]);
- } else {
- fileDo.setParentPath(StringConstants.SLASH);
- }
- fileDo.setAbsPath(tempAbsPath);
- fileDo.setExtension("dir");
- fileDo.setContentType("");
- fileDo.setType(FileTypeEnum.DIR);
- fileDo.setSha256("");
- fileDo.setStorageId(storage.getId());
- baseMapper.insert(fileDo);
- return new IdResp<>(fileDo.getId());
+ FileDO file = new FileDO();
+ file.setName(req.getOriginalName());
+ file.setOriginalName(req.getOriginalName());
+ file.setSize(0L);
+ file.setPath(req.getPath());
+ file.setType(FileTypeEnum.DIR);
+ file.setStorageId(storage.getId());
+ baseMapper.insert(file);
+ return file.getId();
}
@Override
protected void fill(Object obj) {
super.fill(obj);
- if (obj instanceof FileResp fileResp && !URLUtils.isHttpUrl(fileResp.getUrl())) {
+ if (obj instanceof FileResp fileResp) {
StorageDO storage = storageService.getById(fileResp.getStorageId());
- String prefix = StrUtil.appendIfMissing(storage.getDomain(), StringConstants.SLASH);
- String url = URLUtil.normalize(prefix + fileResp.getUrl());
+ String prefix = StrUtil.blankToDefault(storage.getDomain(), storage.getEndpoint());
+ String path = fileResp.getPath();
+ String url = URLUtil.normalize(prefix + path + StringConstants.SLASH + fileResp.getName(), false, true);
fileResp.setUrl(url);
- String thumbnailUrl = StrUtils.blankToDefault(fileResp.getThumbnailUrl(), url, thUrl -> URLUtil
- .normalize(prefix + thUrl));
+ String thumbnailUrl = StrUtils.blankToDefault(fileResp.getThumbnailName(), url, thName -> URLUtil
+ .normalize(prefix + path + StringConstants.SLASH + thName, false, true));
fileResp.setThumbnailUrl(thumbnailUrl);
fileResp.setStorageName("%s (%s)".formatted(storage.getName(), storage.getCode()));
}
@@ -214,4 +217,49 @@ public class FileServiceImpl extends BaseServiceImpl
+ * user/avatar/ => user(path:/)、avatar(path:/user)
+ *
+ *
+ * @param dirPath 路径
+ * @param storage 存储配置
+ */
+ private void createDir(String dirPath, StorageDO storage) {
+ if (StrUtil.isBlank(dirPath) || StringConstants.SLASH.equals(dirPath)) {
+ return;
+ }
+ // user/avatar/ => user:/、avatar:path:/user
+ String[] paths = StrUtil.split(dirPath, StringConstants.SLASH, false, true).toArray(String[]::new);
+ Map pathMap = MapUtil.newHashMap(paths.length, true);
+ for (int i = 0; i < paths.length; i++) {
+ String key = paths[i];
+ String path = (i == 0)
+ ? StringConstants.SLASH
+ : StringConstants.SLASH + String.join(StringConstants.SLASH, Arrays.copyOfRange(paths, 0, i));
+ pathMap.put(key, path);
+ }
+ // 创建文件夹
+ for (Map.Entry entry : pathMap.entrySet()) {
+ String key = entry.getKey();
+ String path = entry.getValue();
+ if (!baseMapper.lambdaQuery()
+ .eq(FileDO::getPath, path)
+ .eq(FileDO::getName, key)
+ .eq(FileDO::getType, FileTypeEnum.DIR)
+ .exists()) {
+ FileDO file = new FileDO();
+ file.setName(key);
+ file.setOriginalName(key);
+ file.setSize(0L);
+ file.setPath(path);
+ file.setType(FileTypeEnum.DIR);
+ file.setStorageId(storage.getId());
+ baseMapper.insert(file);
+ }
+ }
+ }
}
\ No newline at end of file
diff --git a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/StorageServiceImpl.java b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/StorageServiceImpl.java
index 88adf51f..70fcde18 100644
--- a/continew-module-system/src/main/java/top/continew/admin/system/service/impl/StorageServiceImpl.java
+++ b/continew-module-system/src/main/java/top/continew/admin/system/service/impl/StorageServiceImpl.java
@@ -179,7 +179,7 @@ public class StorageServiceImpl extends BaseServiceImpl {
FileStorageProperties.AmazonS3Config config = new FileStorageProperties.AmazonS3Config();
diff --git a/continew-webapi/src/main/java/top/continew/admin/controller/system/FileController.java b/continew-webapi/src/main/java/top/continew/admin/controller/system/FileController.java
index 0611b2d0..6193cf5b 100644
--- a/continew-webapi/src/main/java/top/continew/admin/controller/system/FileController.java
+++ b/continew-webapi/src/main/java/top/continew/admin/controller/system/FileController.java
@@ -61,9 +61,8 @@ public class FileController extends BaseController createDir(@RequestBody FileReq req) {
- return baseService.createDir(req);
+ return new IdResp<>(baseService.createDir(req));
}
}
\ No newline at end of file
diff --git a/continew-webapi/src/main/resources/db/changelog/mysql/main_data.sql b/continew-webapi/src/main/resources/db/changelog/mysql/main_data.sql
index fc4296d3..c950a8bd 100644
--- a/continew-webapi/src/main/resources/db/changelog/mysql/main_data.sql
+++ b/continew-webapi/src/main/resources/db/changelog/mysql/main_data.sql
@@ -273,8 +273,8 @@ INSERT INTO `sys_role_dept` (`role_id`, `dept_id`) VALUES (547888897925840927, 5
INSERT INTO `sys_storage`
(`id`, `name`, `code`, `type`, `access_key`, `secret_key`, `endpoint`, `bucket_name`, `domain`, `description`, `is_default`, `sort`, `status`, `create_user`, `create_time`)
VALUES
-(1, '开发环境', 'local_dev', 1, NULL, NULL, NULL, 'C:/continew-admin/data/file/', 'http://localhost:8000/file', '本地存储', b'1', 1, 1, 1, NOW()),
-(2, '生产环境', 'local_prod', 1, NULL, NULL, NULL, '../data/file/', 'http://api.continew.top/file', '本地存储', b'0', 2, 2, 1, NOW());
+(1, '开发环境', 'local_dev', 1, NULL, NULL, NULL, 'C:/continew-admin/data/file/', 'http://localhost:8000/file/', '本地存储', b'1', 1, 1, 1, NOW()),
+(2, '生产环境', 'local_prod', 1, NULL, NULL, NULL, '../data/file/', 'http://api.continew.top/file/', '本地存储', b'0', 2, 2, 1, NOW());
-- 初始化客户端数据
INSERT INTO `sys_client`
diff --git a/continew-webapi/src/main/resources/db/changelog/mysql/main_table.sql b/continew-webapi/src/main/resources/db/changelog/mysql/main_table.sql
index e198df05..4911cb74 100644
--- a/continew-webapi/src/main/resources/db/changelog/mysql/main_table.sql
+++ b/continew-webapi/src/main/resources/db/changelog/mysql/main_table.sql
@@ -260,8 +260,8 @@ CREATE TABLE IF NOT EXISTS `sys_storage` (
`access_key` varchar(255) DEFAULT NULL COMMENT 'Access Key',
`secret_key` varchar(255) DEFAULT NULL COMMENT 'Secret Key',
`endpoint` varchar(255) DEFAULT NULL COMMENT 'Endpoint',
- `bucket_name` varchar(255) DEFAULT NULL COMMENT 'Bucket',
- `domain` varchar(255) NOT NULL DEFAULT '' COMMENT '域名',
+ `bucket_name` varchar(255) NOT NULL COMMENT 'Bucket',
+ `domain` varchar(255) DEFAULT NULL COMMENT '域名',
`description` varchar(200) DEFAULT NULL COMMENT '描述',
`is_default` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否为默认存储',
`sort` int NOT NULL DEFAULT 999 COMMENT '排序',
@@ -279,17 +279,16 @@ CREATE TABLE IF NOT EXISTS `sys_storage` (
CREATE TABLE IF NOT EXISTS `sys_file` (
`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'ID',
`name` varchar(255) NOT NULL COMMENT '名称',
+ `original_name` varchar(255) NOT NULL COMMENT '原始名称',
`size` bigint(20) NOT NULL COMMENT '大小(字节)',
- `url` varchar(512) NOT NULL COMMENT 'URL',
- `parent_path` varchar(512) DEFAULT '/' COMMENT '上级目录',
- `abs_path` varchar(1024) NOT NULL COMMENT '绝对路径',
- `extension` varchar(100) DEFAULT NULL COMMENT '扩展名',
- `content_type` varchar(255) NOT NULL COMMENT '内容类型',
+ `path` varchar(512) NOT NULL COMMENT '存储路径',
+ `extension` varchar(32) DEFAULT NULL COMMENT '扩展名',
+ `content_type` varchar(255) DEFAULT NULL COMMENT '内容类型',
`type` tinyint(1) UNSIGNED NOT NULL DEFAULT 1 COMMENT '类型(0: 目录;1:其他;2:图片;3:文档;4:视频;5:音频)',
- `sha256` varchar(256) NOT NULL COMMENT 'SHA256值',
+ `sha256` varchar(256) DEFAULT NULL COMMENT 'SHA256值',
`metadata` text DEFAULT NULL COMMENT '元数据',
+ `thumbnail_name` varchar(255) DEFAULT NULL COMMENT '缩略图名称',
`thumbnail_size` bigint(20) DEFAULT NULL COMMENT '缩略图大小(字节)',
- `thumbnail_url` varchar(512) DEFAULT NULL COMMENT '缩略图URL',
`thumbnail_metadata` text DEFAULT NULL COMMENT '缩略图元数据',
`storage_id` bigint(20) NOT NULL COMMENT '存储ID',
`create_user` bigint(20) NOT NULL COMMENT '创建人',
@@ -297,9 +296,8 @@ CREATE TABLE IF NOT EXISTS `sys_file` (
`update_user` bigint(20) DEFAULT NULL COMMENT '修改人',
`update_time` datetime DEFAULT NULL COMMENT '修改时间',
PRIMARY KEY (`id`),
- INDEX `idx_url`(`url`),
- INDEX `idx_sha256`(`sha256`),
INDEX `idx_type`(`type`),
+ INDEX `idx_sha256`(`sha256`),
INDEX `idx_storage_id`(`storage_id`),
INDEX `idx_create_user`(`create_user`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='文件表';
diff --git a/continew-webapi/src/main/resources/db/changelog/postgresql/main_data.sql b/continew-webapi/src/main/resources/db/changelog/postgresql/main_data.sql
index bded0c0e..53f9870e 100644
--- a/continew-webapi/src/main/resources/db/changelog/postgresql/main_data.sql
+++ b/continew-webapi/src/main/resources/db/changelog/postgresql/main_data.sql
@@ -273,8 +273,8 @@ INSERT INTO "sys_role_dept" ("role_id", "dept_id") VALUES (547888897925840927, 5
INSERT INTO "sys_storage"
("id", "name", "code", "type", "access_key", "secret_key", "endpoint", "bucket_name", "domain", "description", "is_default", "sort", "status", "create_user", "create_time")
VALUES
-(1, '开发环境', 'local_dev', 1, NULL, NULL, NULL, 'C:/continew-admin/data/file/', 'http://localhost:8000/file', '本地存储', true, 1, 1, 1, NOW()),
-(2, '生产环境', 'local_prod', 1, NULL, NULL, NULL, '../data/file/', 'http://api.continew.top/file', '本地存储', false, 2, 2, 1, NOW());
+(1, '开发环境', 'local_dev', 1, NULL, NULL, NULL, 'C:/continew-admin/data/file/', 'http://localhost:8000/file/', '本地存储', true, 1, 1, 1, NOW()),
+(2, '生产环境', 'local_prod', 1, NULL, NULL, NULL, '../data/file/', 'http://api.continew.top/file/', '本地存储', false, 2, 2, 1, NOW());
-- 初始化客户端数据
INSERT INTO "sys_client"
diff --git a/continew-webapi/src/main/resources/db/changelog/postgresql/main_table.sql b/continew-webapi/src/main/resources/db/changelog/postgresql/main_table.sql
index 42218f01..bd640481 100644
--- a/continew-webapi/src/main/resources/db/changelog/postgresql/main_table.sql
+++ b/continew-webapi/src/main/resources/db/changelog/postgresql/main_table.sql
@@ -428,8 +428,8 @@ CREATE TABLE IF NOT EXISTS "sys_storage" (
"access_key" varchar(255) DEFAULT NULL,
"secret_key" varchar(255) DEFAULT NULL,
"endpoint" varchar(255) DEFAULT NULL,
- "bucket_name" varchar(255) DEFAULT NULL,
- "domain" varchar(255) NOT NULL DEFAULT '',
+ "bucket_name" varchar(255) NOT NULL,
+ "domain" varchar(255) DEFAULT NULL,
"description" varchar(200) DEFAULT NULL,
"is_default" bool NOT NULL DEFAULT false,
"sort" int4 NOT NULL DEFAULT 999,
@@ -465,17 +465,16 @@ COMMENT ON TABLE "sys_storage" IS '存储表';
CREATE TABLE IF NOT EXISTS "sys_file" (
"id" int8 NOT NULL,
"name" varchar(255) NOT NULL,
+ "original_name" varchar(255) NOT NULL,
"size" int8 NOT NULL,
- "url" varchar(512) NOT NULL,
- "parent_path" varchar(512) NOT NULL DEFAULT '/',
- "abs_path" varchar(512) NOT NULL,
+ "path" varchar(512) NOT NULL,
"extension" varchar(100) DEFAULT NULL,
- "content_type" varchar(255) NOT NULL,
+ "content_type" varchar(255) DEFAULT NULL,
"type" int2 NOT NULL DEFAULT 1,
"sha256" varchar(256) NOT NULL,
"metadata" text DEFAULT NULL,
+ "thumbnail_name" varchar(255) DEFAULT NULL,
"thumbnail_size" int8 DEFAULT NULL,
- "thumbnail_url" varchar(512) DEFAULT NULL,
"thumbnail_metadata" text DEFAULT NULL,
"storage_id" int8 NOT NULL,
"create_user" int8 NOT NULL,
@@ -484,24 +483,22 @@ CREATE TABLE IF NOT EXISTS "sys_file" (
"update_time" timestamp DEFAULT NULL,
PRIMARY KEY ("id")
);
-CREATE INDEX "idx_file_url" ON "sys_file" ("url");
CREATE INDEX "idx_file_type" ON "sys_file" ("type");
CREATE INDEX "idx_file_sha256" ON "sys_file" ("sha256");
CREATE INDEX "idx_file_storage_id" ON "sys_file" ("storage_id");
CREATE INDEX "idx_file_create_user" ON "sys_file" ("create_user");
COMMENT ON COLUMN "sys_file"."id" IS 'ID';
COMMENT ON COLUMN "sys_file"."name" IS '名称';
+COMMENT ON COLUMN "sys_file"."original_name" IS '原始名称';
COMMENT ON COLUMN "sys_file"."size" IS '大小(字节)';
-COMMENT ON COLUMN "sys_file"."url" IS 'URL';
-COMMENT ON COLUMN "sys_file"."parent_path" IS '上级目录';
-COMMENT ON COLUMN "sys_file"."abs_path" IS '绝对路径';
+COMMENT ON COLUMN "sys_file"."path" IS '存储路径';
COMMENT ON COLUMN "sys_file"."extension" IS '扩展名';
COMMENT ON COLUMN "sys_file"."content_type" IS '内容类型';
COMMENT ON COLUMN "sys_file"."type" IS '类型(0: 目录;1:其他;2:图片;3:文档;4:视频;5:音频)';
COMMENT ON COLUMN "sys_file"."sha256" IS 'SHA256值';
COMMENT ON COLUMN "sys_file"."metadata" IS '元数据';
+COMMENT ON COLUMN "sys_file"."thumbnail_name" IS '缩略图名称';
COMMENT ON COLUMN "sys_file"."thumbnail_size" IS '缩略图大小(字节)';
-COMMENT ON COLUMN "sys_file"."thumbnail_url" IS '缩略图URL';
COMMENT ON COLUMN "sys_file"."thumbnail_metadata" IS '缩略图元数据';
COMMENT ON COLUMN "sys_file"."storage_id" IS '存储ID';
COMMENT ON COLUMN "sys_file"."create_user" IS '创建人';