Skip to content

Commit 6787b40

Browse files
committed
refactor(system/file): 重构文件管理相关代码,完善文件夹场景
1 parent bc057da commit 6787b40

File tree

14 files changed

+216
-152
lines changed

14 files changed

+216
-152
lines changed

continew-module-system/src/main/java/top/continew/admin/system/config/file/FileRecorderImpl.java

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,11 @@
1616

1717
package top.continew.admin.system.config.file;
1818

19+
import cn.hutool.core.collection.CollUtil;
1920
import cn.hutool.core.util.ClassUtil;
2021
import cn.hutool.core.util.StrUtil;
2122
import cn.hutool.core.util.URLUtil;
23+
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
2224
import lombok.RequiredArgsConstructor;
2325
import lombok.extern.slf4j.Slf4j;
2426
import org.dromara.x.file.storage.core.FileInfo;
@@ -32,7 +34,9 @@
3234
import top.continew.starter.core.constant.StringConstants;
3335
import top.continew.starter.core.util.URLUtils;
3436

35-
import java.util.Optional;
37+
import java.util.List;
38+
import java.util.Map;
39+
import java.util.stream.Collectors;
3640

3741
/**
3842
* 文件记录实现类
@@ -58,11 +62,11 @@ public boolean save(FileInfo fileInfo) {
5862
// 方便文件上传完成后获取文件信息
5963
fileInfo.setId(String.valueOf(file.getId()));
6064
if (!URLUtils.isHttpUrl(fileInfo.getUrl())) {
61-
String prefix = StrUtil.appendIfMissing(storage.getDomain(), StringConstants.SLASH);
62-
String url = URLUtil.normalize(prefix + fileInfo.getUrl());
65+
String prefix = StrUtil.blankToDefault(storage.getDomain(), storage.getEndpoint());
66+
String url = URLUtil.completeUrl(prefix, fileInfo.getUrl());
6367
fileInfo.setUrl(url);
6468
if (StrUtil.isNotBlank(fileInfo.getThUrl())) {
65-
fileInfo.setThUrl(URLUtil.normalize(prefix + fileInfo.getThUrl()));
69+
fileInfo.setThUrl(URLUtil.completeUrl(prefix, fileInfo.getThUrl()));
6670
}
6771
}
6872
return true;
@@ -81,7 +85,10 @@ public FileInfo getByUrl(String url) {
8185
@Override
8286
public boolean delete(String url) {
8387
FileDO file = this.getFileByUrl(url);
84-
return fileMapper.lambdaUpdate().eq(FileDO::getUrl, file.getUrl()).remove();
88+
if (null == file) {
89+
return false;
90+
}
91+
return fileMapper.lambdaUpdate().eq(FileDO::getId, file.getId()).remove();
8592
}
8693

8794
@Override
@@ -106,10 +113,32 @@ public void deleteFilePartByUploadId(String s) {
106113
* @return 文件信息
107114
*/
108115
private FileDO getFileByUrl(String url) {
109-
Optional<FileDO> fileOptional = fileMapper.lambdaQuery().eq(FileDO::getUrl, url).oneOpt();
110-
return fileOptional.orElseGet(() -> fileMapper.lambdaQuery()
111-
.likeLeft(FileDO::getUrl, StrUtil.subAfter(url, StringConstants.SLASH, true))
112-
.oneOpt()
113-
.orElse(null));
116+
LambdaQueryChainWrapper<FileDO> queryWrapper = fileMapper.lambdaQuery()
117+
.eq(FileDO::getName, StrUtil.subAfter(url, StringConstants.SLASH, true));
118+
// 非 HTTP URL 场景
119+
if (!URLUtils.isHttpUrl(url)) {
120+
return queryWrapper.eq(FileDO::getPath, StrUtil.prependIfMissing(StrUtil
121+
.subBefore(url, StringConstants.SLASH, true), StringConstants.SLASH)).one();
122+
}
123+
// HTTP URL 场景
124+
List<FileDO> list = queryWrapper.list();
125+
if (CollUtil.isEmpty(list)) {
126+
return null;
127+
}
128+
if (list.size() == 1) {
129+
return list.get(0);
130+
}
131+
// 结合存储配置进行匹配
132+
List<StorageDO> storageList = storageMapper.selectByIds(list.stream().map(FileDO::getStorageId).toList());
133+
Map<Long, StorageDO> storageMap = storageList.stream()
134+
.collect(Collectors.toMap(StorageDO::getId, storage -> storage));
135+
return list.stream().filter(file -> {
136+
// http://localhost:8000/file/user/avatar/6825e687db4174e7a297a5f8.png => http://localhost:8000/file/user/avatar
137+
String urlPrefix = StrUtil.subBefore(url, StringConstants.SLASH, true);
138+
// http://localhost:8000/file/ + /user/avatar => http://localhost:8000/file/user/avatar
139+
StorageDO storage = storageMap.get(file.getStorageId());
140+
String prefix = StrUtil.blankToDefault(storage.getDomain(), storage.getEndpoint());
141+
return urlPrefix.equals(URLUtil.normalize(prefix + file.getPath(), false, true));
142+
}).findFirst().orElse(null);
114143
}
115144
}

continew-module-system/src/main/java/top/continew/admin/system/config/file/FileStorageConfigLoader.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package top.continew.admin.system.config.file;
1818

19-
import cn.hutool.core.bean.BeanUtil;
2019
import cn.hutool.core.collection.CollUtil;
2120
import lombok.RequiredArgsConstructor;
2221
import lombok.extern.slf4j.Slf4j;
@@ -25,8 +24,6 @@
2524
import org.springframework.stereotype.Component;
2625
import top.continew.admin.common.enums.DisEnableStatusEnum;
2726
import top.continew.admin.system.model.entity.StorageDO;
28-
import top.continew.admin.system.model.query.StorageQuery;
29-
import top.continew.admin.system.model.resp.StorageResp;
3027
import top.continew.admin.system.service.StorageService;
3128

3229
import java.util.List;
@@ -46,12 +43,10 @@ public class FileStorageConfigLoader implements ApplicationRunner {
4643

4744
@Override
4845
public void run(ApplicationArguments args) {
49-
StorageQuery query = new StorageQuery();
50-
query.setStatus(DisEnableStatusEnum.ENABLE);
51-
List<StorageResp> storageList = storageService.list(query, null);
52-
if (CollUtil.isEmpty(storageList)) {
46+
List<StorageDO> list = storageService.lambdaQuery().eq(StorageDO::getStatus, DisEnableStatusEnum.ENABLE).list();
47+
if (CollUtil.isEmpty(list)) {
5348
return;
5449
}
55-
storageList.forEach(storage -> storageService.load(BeanUtil.copyProperties(storage, StorageDO.class)));
50+
list.forEach(storageService::load);
5651
}
5752
}

continew-module-system/src/main/java/top/continew/admin/system/enums/StorageTypeEnum.java

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,15 @@ public enum StorageTypeEnum implements BaseEnum<Integer> {
4343
@Override
4444
public void validate(StorageReq req) {
4545
ValidationUtils.validate(req, ValidationGroup.Storage.Local.class);
46-
ValidationUtils.throwIf(!URLUtils.isHttpUrl(req.getDomain()), "访问路径格式不正确");
46+
ValidationUtils.throwIf(StrUtil.isNotBlank(req.getDomain()) && !URLUtils.isHttpUrl(req
47+
.getDomain()), "访问路径格式不正确");
4748
}
4849

4950
@Override
5051
public void pretreatment(StorageReq req) {
5152
super.pretreatment(req);
52-
req.setBucketName(StrUtil.appendIfMissing(req.getBucketName()
53-
.replace(StringConstants.BACKSLASH, StringConstants.SLASH), StringConstants.SLASH));
53+
// 本地存储路径需要以 “/” 结尾
54+
req.setBucketName(StrUtil.appendIfMissing(req.getBucketName(), StringConstants.SLASH));
5455
}
5556
},
5657

@@ -61,7 +62,8 @@ public void pretreatment(StorageReq req) {
6162
@Override
6263
public void validate(StorageReq req) {
6364
ValidationUtils.validate(req, ValidationGroup.Storage.OSS.class);
64-
ValidationUtils.throwIf(!URLUtils.isHttpUrl(req.getDomain()), "域名格式不正确");
65+
ValidationUtils.throwIf(StrUtil.isNotBlank(req.getDomain()) && !URLUtils.isHttpUrl(req
66+
.getDomain()), "域名格式不正确");
6567
}
6668
};
6769

@@ -81,6 +83,9 @@ public void validate(StorageReq req) {
8183
* @param req 请求参数
8284
*/
8385
public void pretreatment(StorageReq req) {
84-
req.setDomain(StrUtil.removeSuffix(req.getDomain(), StringConstants.SLASH));
86+
// 域名需要以 “/” 结尾
87+
if (StrUtil.isNotBlank(req.getDomain())) {
88+
req.setDomain(StrUtil.appendIfMissing(req.getDomain(), StringConstants.SLASH));
89+
}
8590
}
8691
}

continew-module-system/src/main/java/top/continew/admin/system/model/entity/FileDO.java

Lines changed: 27 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@
1717
package top.continew.admin.system.model.entity;
1818

1919
import cn.hutool.core.date.DateUtil;
20-
import cn.hutool.core.io.file.FileNameUtil;
21-
import cn.hutool.core.util.EscapeUtil;
2220
import cn.hutool.core.util.StrUtil;
2321
import cn.hutool.json.JSONUtil;
2422
import com.baomidou.mybatisplus.annotation.TableName;
@@ -28,7 +26,6 @@
2826
import top.continew.admin.common.model.entity.BaseDO;
2927
import top.continew.admin.system.enums.FileTypeEnum;
3028
import top.continew.starter.core.constant.StringConstants;
31-
import top.continew.starter.core.util.StrUtils;
3229

3330
import java.io.Serial;
3431
import java.util.Map;
@@ -53,24 +50,19 @@ public class FileDO extends BaseDO {
5350
private String name;
5451

5552
/**
56-
* 大小(字节)
57-
*/
58-
private Long size;
59-
60-
/**
61-
* URL
53+
* 原始名称
6254
*/
63-
private String url;
55+
private String originalName;
6456

6557
/**
66-
* 上级目录
58+
* 大小(字节)
6759
*/
68-
private String parentPath;
60+
private Long size;
6961

7062
/**
71-
* 绝对路径
63+
* 存储路径
7264
*/
73-
private String absPath;
65+
private String path;
7466

7567
/**
7668
* 扩展名
@@ -88,7 +80,7 @@ public class FileDO extends BaseDO {
8880
private FileTypeEnum type;
8981

9082
/**
91-
* SHA256值
83+
* SHA256 值
9284
*/
9385
private String sha256;
9486

@@ -98,14 +90,14 @@ public class FileDO extends BaseDO {
9890
private String metadata;
9991

10092
/**
101-
* 缩略图大小(字节)
93+
* 缩略图名称
10294
*/
103-
private Long thumbnailSize;
95+
private String thumbnailName;
10496

10597
/**
106-
* 缩略图 URL
98+
* 缩略图大小(字节)
10799
*/
108-
private String thumbnailUrl;
100+
private Long thumbnailSize;
109101

110102
/**
111103
* 缩略图元数据
@@ -123,21 +115,21 @@ public class FileDO extends BaseDO {
123115
* @param fileInfo {@link FileInfo} 文件信息
124116
*/
125117
public FileDO(FileInfo fileInfo) {
126-
this.name = FileNameUtil.getPrefix(EscapeUtil.unescape(fileInfo.getOriginalFilename()));
118+
this.name = fileInfo.getFilename();
119+
this.originalName = fileInfo.getOriginalFilename();
127120
this.size = fileInfo.getSize();
128-
this.url = fileInfo.getUrl();
129-
this.absPath = StrUtil.isEmpty(fileInfo.getPath())
121+
// 如果为空,则为 /;如果不为空,则调整格式为:/xxx
122+
this.path = StrUtil.isEmpty(fileInfo.getPath())
130123
? StringConstants.SLASH
131-
: StrUtil.prependIfMissing(fileInfo.getPath(), StringConstants.SLASH);
132-
String[] pathAttr = this.absPath.split(StringConstants.SLASH);
133-
this.parentPath = pathAttr.length > 1 ? pathAttr[pathAttr.length - 1] : StringConstants.SLASH;
124+
: StrUtil.removeSuffix(StrUtil.prependIfMissing(fileInfo
125+
.getPath(), StringConstants.SLASH), StringConstants.SLASH);
134126
this.extension = fileInfo.getExt();
135127
this.contentType = fileInfo.getContentType();
136128
this.type = FileTypeEnum.getByExtension(this.extension);
137129
this.sha256 = fileInfo.getHashInfo().getSha256();
138130
this.metadata = JSONUtil.toJsonStr(fileInfo.getMetadata());
131+
this.thumbnailName = fileInfo.getThFilename();
139132
this.thumbnailSize = fileInfo.getThSize();
140-
this.thumbnailUrl = fileInfo.getThUrl();
141133
this.thumbnailMetadata = JSONUtil.toJsonStr(fileInfo.getThMetadata());
142134
this.setCreateTime(DateUtil.toLocalDateTime(fileInfo.getCreateTime()));
143135
}
@@ -151,27 +143,24 @@ public FileDO(FileInfo fileInfo) {
151143
public FileInfo toFileInfo(StorageDO storage) {
152144
FileInfo fileInfo = new FileInfo();
153145
fileInfo.setPlatform(storage.getCode());
154-
fileInfo.setFilename(StrUtil.contains(this.url, StringConstants.SLASH)
155-
? StrUtil.subAfter(this.url, StringConstants.SLASH, true)
156-
: this.url);
157-
fileInfo.setOriginalFilename(StrUtils
158-
.blankToDefault(this.extension, this.name, ex -> this.name + StringConstants.DOT + ex));
146+
fileInfo.setFilename(this.name);
147+
fileInfo.setOriginalFilename(this.originalName);
148+
// 暂不使用,所以保持空
159149
fileInfo.setBasePath(StringConstants.EMPTY);
160150
fileInfo.setSize(this.size);
161-
fileInfo.setUrl(this.url);
162-
fileInfo.setPath(StringConstants.SLASH.equals(this.absPath)
151+
fileInfo.setPath(StringConstants.SLASH.equals(this.path)
163152
? StringConstants.EMPTY
164-
: StrUtil.removePrefix(this.absPath, StringConstants.SLASH));
153+
: StrUtil.appendIfMissing(StrUtil.removePrefix(this.path, StringConstants.SLASH), StringConstants.SLASH));
165154
fileInfo.setExt(this.extension);
155+
fileInfo.setContentType(this.contentType);
166156
if (StrUtil.isNotBlank(this.metadata)) {
167157
fileInfo.setMetadata(JSONUtil.toBean(this.metadata, Map.class));
168158
}
159+
fileInfo.setUrl(fileInfo.getPath() + fileInfo.getFilename());
169160
// 缩略图信息
170-
fileInfo.setThFilename(StrUtil.contains(this.thumbnailUrl, StringConstants.SLASH)
171-
? StrUtil.subAfter(this.thumbnailUrl, StringConstants.SLASH, true)
172-
: this.thumbnailUrl);
161+
fileInfo.setThFilename(this.thumbnailName);
173162
fileInfo.setThSize(this.thumbnailSize);
174-
fileInfo.setThUrl(this.thumbnailUrl);
163+
fileInfo.setThUrl(fileInfo.getPath() + fileInfo.getThFilename());
175164
if (StrUtil.isNotBlank(this.thumbnailMetadata)) {
176165
fileInfo.setThMetadata(JSONUtil.toBean(this.thumbnailMetadata, Map.class));
177166
}

continew-module-system/src/main/java/top/continew/admin/system/model/query/FileQuery.java

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,15 @@ public class FileQuery implements Serializable {
4141
/**
4242
* 名称
4343
*/
44-
@Schema(description = "名称", example = "图片")
44+
@Schema(description = "名称", example = "example")
4545
@Query(type = QueryType.LIKE)
46-
private String name;
46+
private String originalName;
4747

4848
/**
49-
* 绝对路径
49+
* 存储路径
5050
*/
51-
@Schema(description = "绝对路径", example = "/2025")
52-
@Query(type = QueryType.EQ)
53-
private String absPath;
51+
@Schema(description = "存储路径", example = "/")
52+
private String path;
5453

5554
/**
5655
* 类型

continew-module-system/src/main/java/top/continew/admin/system/model/req/FileReq.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,14 +40,14 @@ public class FileReq implements Serializable {
4040
/**
4141
* 名称
4242
*/
43-
@Schema(description = "名称", example = "test123")
43+
@Schema(description = "名称", example = "example")
4444
@NotBlank(message = "文件名称不能为空")
4545
@Length(max = 255, message = "文件名称长度不能超过 {max} 个字符")
46-
private String name;
46+
private String originalName;
4747

4848
/**
49-
* 上级目录
49+
* 存储路径
5050
*/
51-
@Schema(description = "上级目录", example = "25")
52-
private String parentPath;
51+
@Schema(description = "存储路径", example = "/")
52+
private String path;
5353
}

0 commit comments

Comments
 (0)