SpringBoot + Minio 自动清理历史文件实战手册:从入门到高可用
一、为什么要“自动清理”?
Minio 默认“只存不删”,三个月的测试包、过期的临时二维码、同事手抖传上来的 10G 日志,把磁盘撑爆只是时间问题。
手动删?——凌晨 3 点爬起来 mc rm 的酸爽,谁删谁知道。
所以,“让代码替我们熬夜” 才是正道:SpringBoot 定时任务 + Minio,每天凌晨自动扫盘、批量删、失败还能钉钉喊我起床。
二、整体架构(一张图记住)
┌-------------┐ ┌-------------┐ ┌-------------┐
│ SpringBoot │────▶│ Redis │────▶│ 分布式锁 │ 集群只删一次
│ @Scheduled │ └-------------┘ └-------------┘
│ /Quartz │ ▲
└------┬-------┘ │
│ list/delete │
▼ │
┌-------------┐ ┌-------------┐
│ Minio │◀────┤ Nacos配置 │ 动态改规则 0 重启
└-------------┘ └-------------┘
▲
│ fail
▼
┌-------------┐
│ 钉钉机器人 │ 失败实时@全员
└-------------┘
三、30 分钟落地:最小可用版本
1. 启动 Minio(Docker 一行)
docker run -d --name minio \
-p 9000:9000 -p 9001:9001 \
-v $PWD/data:/data \
-e MINIO_ROOT_USER=minioadmin \
-e MINIO_ROOT_PASSWORD=minioadmin \
minio/minio server /data --console-address ":9001"
浏览器打开 http://localhost:9001 建个桶 file-bucket。
2. SpringBoot 引入依赖
<!-- Minio -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.2</version>
</dependency>
<!-- 定时任务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-quartz</artifactId>
</dependency>
<!-- 工具类 -->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.20</version>
</dependency>
3. 配置 application.yml
minio:
endpoint: http://localhost:9000
access-key: minioadmin
secret-key: minioadmin
bucket-name: file-bucket
clean:
enabled: true
cron: 0 0 2 * * ? # 每天 02:00
expire-days: 30 # 30 天前
ignore-prefixes: test_,temp_
max-batch-size: 100
4. 封装 Minio 工具类(核心代码)
@Component
@RequiredArgsConstructor
@Slf4j
public class MinioUtils {
private final MinioClient minioClient;
private final MinioConfig cfg;
/** 列出过期文件 */
public List<String> listExpired(int days, Set<String> ignore) {
long expireMillis = days * 24 * 60 * 60 * 1000L;
long now = System.currentTimeMillis();
List<String> result = new ArrayList<>();
minioClient.listObjects(ListObjectsArgs.builder()
.bucket(cfg.getBucketName())
.recursive(true).build())
.forEach(r -> {
try {
Item i = r.get();
if (i.isDir()) return;
String name = i.objectName();
if (ignore.stream().anyMatch(name::startsWith)) return;
if (now - i.lastModified().getTime() > expireMillis) {
result.add(name);
}
} catch (Exception e) {
log.warn("list error", e);
}
});
return result;
}
/** 批量删除 */
public void deleteQuietly(List<String> names, int batch) {
Lists.partition(names, batch).forEach(part -> {
List<DeleteObject> dels = part.stream().map(DeleteObject::new).toList();
minioClient.removeObjects(RemoveObjectsArgs.builder()
.bucket(cfg.getBucketName())
.objects(dels).build())
.forEach(r -> {
try { r.get(); } catch (Exception e) {
log.warn("delete error:{}", e.getMessage());
}
});
});
}
}
5. 定时任务(@Scheduled 版)
@Component
@ConditionalOnProperty(prefix = "minio.clean", name = "enabled", havingValue = "true")
@Slf4j
@RequiredArgsConstructor
public class MinioCleanTask {
private final MinioUtils minioUtils;
private final MinioConfig config;
@Scheduled(cron = "${minio.clean.cron}")
public void clean() {
var c = config.getClean();
var expired = minioUtils.listExpired(c.getExpireDays(),
Set.of(c.getIgnorePrefixes().split(",")));
if (expired.isEmpty()) return;
minioUtils.deleteQuietly(expired, c.getMaxBatchSize());
log.info("clean finish, size={}", expired.size());
}
}
启动类加 @EnableScheduling,完事。
跑 30 秒 Cron 自测:0/30 * * * * ?,控制台看日志,文件说删就删。
四、生产加固:高可用 4 件套
| 问题 | 解决方案 | 一句话口诀 |
|---|---|---|
| 集群重复执行 | Redis 分布式锁 | “谁抢到谁干活” |
| 改配置就重启 | Nacos + @RefreshScope |
“配置热刷新” |
| 失败不知道 | 钉钉/企微机器人 | “删失败就@人” |
| 想暂停/改时间 | Quartz JDBC 模式 | “任务进库,动态改 Cron” |
代码全部在原文里,直接 Copy 即可跑,这里只给思路,不再占篇幅。
五、性能小贴士
-
大桶先分页
Minio 的listObjects默认 1000 条一页,百万文件时把maxKeys(500)调小,防止一次性返回把内存打满。 -
删除再并发
批量删除最多一次 1000 条,建议 100~200 一批;若量特别大,可把deleteQuietly用线程池并行删,CPU 打满比 IO 等死强。 -
先备份再删
重要业务桶,清理前把文件copyObject到backup-bucket,确认无误后 7 天再删备份,给自己一条“后悔药”。 -
灰度放量
首次上线把expire-days设成 180,观察一周没问题再降到 90→60→30,“步子小,不背锅”。
六、一行命令快速验证
# 造 1 万个 30 天前的空文件
for i in {1..10000}; do
touch -d "2024-08-01" /tmp/dummy$i.txt
done
mc cp --recursive /tmp/ minio/file-bucket/30days/
# 30 秒后看日志,1 万文件被扫光
七、结语
把这篇代码丢到你的项目里,10 分钟就能拥有一个不会罢工的“Minio 保洁阿姨”。
从此磁盘告警是别人的故事,你只管安心睡觉,让 SpringBoot 和 Minio 在深夜帮你把垃圾带走。
“代码写得早,运维不熬夜!”
更多推荐

所有评论(0)