『博客开发日记-后台』之删除文件接口的实现

本文最后更新于 2026年5月30日 下午

删除文件接口的实现


删除文件接口的需求

检测文件是否为空

先获得要删除文件的id列表

再检查要删除的文件是否存在,如果列表中要删除的文件不纯在则删除失败

然后根据选中的文件URL查询相关文章(获取文章中的缩略图,获取文章内容中的图片)

检验选中的文件中是否能被删除

如果文件正在被文章(包含缩略图或内容)使用,不允许删除

如果文件正在被用户头像使用,不允许删除

如果文件正在被通知中使用,不允许删除

最后从oss中删除(移动到delete/文件夹内)


代码实现

SysFileServiceImpl 里的方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
//删除文件(批量)
@Override
@Transactional
public ResponseResult deleteFile(Long[] ids)
{
//检测文件是否为空
if (ids == null || ids.length == 0) {
return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "请选择要删除的文件");
}

//获得要删除文件的id列表
List<Long> idList = List.of(ids);
List<SysFile> filesToDelete = listByIds(idList);

//检查要删除的文件是否存在,如果列表中要删除的文件不纯在则删除失败
if (filesToDelete.size() != idList.size())
{
Set<Long> existIds = filesToDelete.stream()
.map(SysFile::getId)
.collect(Collectors.toSet());

//找出不存在的 文件id
List<Long> notExistIds = idList.stream()
.filter(id -> !existIds.contains(id))
.collect(Collectors.toList());

return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "删除失败,以下文件ID不存在:" + notExistIds);
}

//根据选中的文件URL查询相关文章和通知
List<String> fileUrls = filesToDelete.stream()
.map(SysFile::getUrl)
.filter(StringUtils::hasText)
.collect(Collectors.toList());

Map<String, List<Article>> fileArticleUsageMap = new HashMap<>();
Map<String, List<SysNotice>> fileNoticeUsageMap = new HashMap<>();
if (!fileUrls.isEmpty())
{
List<Article> relatedArticles = articleService.list(
Wrappers.<Article>lambdaQuery()
.select(Article::getId, Article::getTitle, Article::getThumbnail, Article::getContent)
.and(wrapper -> {
wrapper.in(Article::getThumbnail, fileUrls);
for (String fileUrl : fileUrls) {
wrapper.or().like(Article::getContent, fileUrl);
}
})
);

for (Article article : relatedArticles)
{
//获取文章中的缩略图
if (StringUtils.hasText(article.getThumbnail()) && fileUrls.contains(article.getThumbnail())) {
fileArticleUsageMap.computeIfAbsent(article.getThumbnail(), key -> new ArrayList<>())
.add(article);
}

//获取文章内容中的图片
List<String> contentImages = ImageUrlUtils.extractImageUrls(article.getContent());
if (!contentImages.isEmpty()) {
for (String imageUrl : contentImages) {
if (StringUtils.hasText(imageUrl) && fileUrls.contains(imageUrl)) {
fileArticleUsageMap.computeIfAbsent(imageUrl, key -> new ArrayList<>())
.add(article);
}
}
}
}

//获取通知里的图片url
List<SysNotice> relatedNotices = sysNoticeService.list(
Wrappers.<SysNotice>lambdaQuery()
.select(SysNotice::getId, SysNotice::getTitle, SysNotice::getContent)
.and(wrapper -> {
for (String fileUrl : fileUrls) {
wrapper.or().like(SysNotice::getContent, fileUrl);
}
})
);

//遍历每一条通知 检查这条通知的内容里有没有图片地址
//如果有就看看这些图片是不是准备删除的文件
for (SysNotice notice : relatedNotices) {
List<String> contentImages = ImageUrlUtils.extractImageUrls(notice.getContent());
if (!contentImages.isEmpty()) {
for (String imageUrl : contentImages) {
if (StringUtils.hasText(imageUrl) && fileUrls.contains(imageUrl)) {
fileNoticeUsageMap.computeIfAbsent(imageUrl, key -> new ArrayList<>())
.add(notice);
}
}
}
}
}

//检验选中的文件中是否能被删除
List<String> errorMsg = new ArrayList<>();
for (SysFile file : filesToDelete)
{
//获取文件url
String fileUrl = file.getUrl();

//如果文件正在被文章(包含缩略图或内容)使用,不允许删除
List<Article> usedArticles = fileArticleUsageMap.get(fileUrl);
if (usedArticles != null && !usedArticles.isEmpty()) {
String articleNames = usedArticles.stream()
.map(Article::getTitle)
.filter(StringUtils::hasText)
.distinct()
.collect(Collectors.joining("、"));
if (!StringUtils.hasText(articleNames)) {
articleNames = usedArticles.stream()
.map(article -> String.valueOf(article.getId()))
.distinct()
.collect(Collectors.joining("、"));
}
errorMsg.add("【" + file.getName() + "】正在被文章【" + articleNames + "】使用");
}

//如果文件正在被通知内容中的图片使用,不允许删除
List<SysNotice> usedNotices = fileNoticeUsageMap.get(fileUrl);
if (usedNotices != null && !usedNotices.isEmpty()) {
String noticeNames = usedNotices.stream()
.map(SysNotice::getTitle)
.filter(StringUtils::hasText)
.distinct()
.collect(Collectors.joining("、"));
if (!StringUtils.hasText(noticeNames)) {
noticeNames = usedNotices.stream()
.map(notice -> String.valueOf(notice.getId()))
.distinct()
.collect(Collectors.joining("、"));
}
errorMsg.add("【" + file.getName() + "】正在被通知【" + noticeNames + "】内容使用");
}

//如果文件正在被用户头像使用,不允许删除
SysUser user = sysUserService.getOne(
Wrappers.<SysUser>lambdaQuery()
.eq(SysUser::getAvatar, fileUrl)
.select(SysUser::getUsername)
);

if (user != null && StringUtils.hasText(user.getUsername())) {
errorMsg.add("【" + file.getName() + "】正在被用户【" + user.getUsername() + "】使用");
}
}

if (!errorMsg.isEmpty()) {
String msg = String.join(";", errorMsg);
return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, msg + ",无法删除");
}

boolean removed = removeByIds(idList);
if (!removed) {
return ResponseResult.errorResult(AppHttpCodeEnum.SYSTEM_ERROR, "文件删除失败");
}

//从oss中删除(移动到delete/文件夹内)
for (SysFile file : filesToDelete) {
if (file != null && StringUtils.hasText(file.getUrl())) {
ossFileService.moveFileToDeleted(file.getUrl());
}
}

return ResponseResult.okResult();
}





PS:该系列只做为作者学习开发项目做的笔记用

不一定符合读者来学习,仅供参考


预告

后续会记录博客的开发过程

每次学习会做一份笔记来进行发表

“一花一世界,一叶一菩提”


版权所有 © 2026 云梦泽
欢迎访问我的个人网站:https://hgt12.github.io/


『博客开发日记-后台』之删除文件接口的实现
http://example.com/2026/05/12/『博客开发日记-后台』之删除文件接口的实现/
作者
云梦泽
发布于
2026年5月12日
更新于
2026年5月30日
许可协议