硬核實戰!SpringBoot + Minio 定時清理,輕松奪回海量存儲空間!
在日常開發中,我們往往選擇 MinIO 作為項目的圖片或文件存儲服務。它不僅兼容 S3 協議,還能在本地快速搭建分布式存儲環境,方便又高效。 但隨著業務增長,存儲在 MinIO 中的圖片會呈現 指數級上漲:活動頁上傳的 Banner、用戶頭像歷史版本、報表導出的臨時文件……一段時間后,它們大多不再被使用,卻依舊占據存儲空間。
如果我們不對這些“過期文件”進行定期清理,不僅存儲成本會增加,還可能影響系統長期運行的可維護性。因此,本文將通過 SpringBoot + MinIO + 定時任務 的方式,實現一個自動清理歷史文件的功能。
最終效果:
- 文件按日期目錄(yyyy-MM-dd/)存儲
- 每月定時任務執行,清理掉早于指定時間的目錄
- 自動釋放存儲空間,降低成本
項目依賴
在 pom.xml 中添加核心依賴即可:
<!-- MinIO SDK -->
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.5.1</version>
</dependency>說明:
- MinIO SDK:與 MinIO 服務交互,支持上傳、下載、刪除等操作
- Spring Boot Starter:內置定時任務支持,免額外引入依賴
核心刪除邏輯
文件的目錄結構約定為:
/bucketName/yyyy-MM-dd/xxx.jpeg也就是說,每天的文件會放到一個獨立的日期目錄下。 因此我們的目標是:刪除早于指定日期的整個目錄。
工具類 MinioUtil
項目路徑:
/src/main/java/com/icoderoad/utils/MinioUtil.java核心方法:
方法簽名 | 作用 | 返回值 | 冪等性 |
| 刪除截止日期前的所有日期目錄 | 實際刪除對象數 | 多次調用結果一致 |
| 刪除單個日期目錄下的對象 | 刪除數量 | 同上 |
關鍵代碼:
/**
* 刪除早于指定日期的所有日期目錄(yyyy-MM-dd/)
*
* @param endExclusive 截止日期(不含)
* @return 實際刪除的對象總數
*/
public int deleteDateFoldersBefore(LocalDate endExclusive) {
if (endExclusive == null) {
throw new IllegalArgumentException("指定日期不能為空");
}
LocalDate today = LocalDate.now();
if (!endExclusive.isBefore(today)) {
return 0;
}
int totalDeleted = 0;
for (LocalDate d = endExclusive.minusDays(1); !d.isBefore(retainSince); d = d.minusDays(1)) {
totalDeleted += deleteSingleFolder(d.format(DateTimeFormatter.ISO_LOCAL_DATE) + "/");
}
return totalDeleted;
}刪除單個目錄;
private int deleteSingleFolder(String prefix) {
try {
List<DeleteObject> objects = new ArrayList<>();
minioClient.listObjects(
ListObjectsArgs.builder().bucket(bucketName).prefix(prefix).recursive(true).build()
).forEach(r -> {
try {
objects.add(new DeleteObject(r.get().objectName()));
} catch (Exception ignored) {
log.warn("文件名獲取失敗");
}
});
if (objects.isEmpty()) {
return 0;
}
Iterable<Result<DeleteError>> results = minioClient.removeObjects(
RemoveObjectsArgs.builder().bucket(bucketName).objects(objects).build()
);
for (Result<DeleteError> res : results) {
res.get(); // 必須觸發懶加載請求
}
return objects.size();
} catch (Exception e) {
log.warn("刪除目錄 {} 失敗: {}", prefix, e.toString());
return 0;
}
}性能與容錯
- 懶加載陷阱:listObjects 與 removeObjects 均是延遲執行,必須遍歷結果才會真正觸發請求
- 批量刪除限制:MinIO 單次請求最多刪除 1000 個對象
- 冪等性設計:重復刪除同一路徑不會報錯,已刪除的對象會被跳過
- 常見錯誤處理:
NoSuchBucket → 啟動時校驗桶
AccessDenied → 確認 AK/SK 權限
SlowDown → 增加退避重試策略
單元測試
路徑:/src/test/java/com/icoderoad/MinioTest.java
@SpringBootTest
public class MinioTest {
@Autowired
private MinioUtil minioUtil;
@Test
public void testDelete() {
int count = minioUtil.deleteDateFoldersBefore(LocalDate.of(2025, 8, 2));
System.out.println("刪除文件數量:" + count);
}
}定時任務配置
啟用定時任務
在啟動類中開啟:
@SpringBootApplication
@EnableScheduling
public class StorageApplication {
public static void main(String[] args) {
SpringApplication.run(StorageApplication.class, args);
}
}定時任務類
路徑:/src/main/java/com/icoderoad/task/MinioCleanTask.java
@Component
@RequiredArgsConstructor
@Slf4j
public class MinioCleanTask {
private final MinioUtil minioUtil;
/**
* 每月 1 號凌晨 3 點清理早于當天的目錄
*/
@Scheduled(cron = "0 0 3 1 * ?")
public void minioClean() {
try {
LocalDate today = LocalDate.now();
log.info("清理任務開始,清理日期:{}", today);
int deleteCount = minioUtil.deleteDateFoldersBefore(today);
log.info("任務完成,共清理 {} 個文件", deleteCount);
} catch (Exception e) {
log.error("MinIO 清理任務失敗", e);
}
}
}Cron 表達式快速回顧
表達式 | 含義 |
| 每分鐘執行一次 |
| 每 5 分鐘執行一次 |
| 每天凌晨 1 點執行 |
| 每月 1 日凌晨 3 點執行 |
結論
通過 SpringBoot + MinIO + 定時任務 的組合,我們實現了一個高效的存儲清理方案:
- 自動化:無需人工干預,定時任務定期清理
- 可控性:基于日期前綴,刪除邏輯清晰,冪等性保證安全
- 擴展性:可靈活配置保留日期與清理策略
這不僅幫助我們 節省了大量存儲成本,還提升了系統的長期可維護性。 對于任何依賴對象存儲的系統而言,這種清理機制都是必不可少的。
























