在日常开发中,我们经常遇到需要定期清理过期文件的场景。比如测试结果目录、日志文件、临时缓存等,这些文件会随着时间推移不断累积,占用大量磁盘空间。手动清理不仅效率低下,还容易遗漏。本文分享一个用C#实现的自动化解决方案,可以精准清理指定目录中超过30天的旧文件。
这个工具特别适合以下场景:
核心功能包括:
判断文件是否过期的核心依据是文件的最后修改时间(LastWriteTime)。相比创建时间(CreationTime),最后修改时间更能反映文件的"活跃程度"。系统设计采用以下逻辑:
提示:为什么选择LastWriteTime而不是CreationTime?因为有些文件可能创建后很久才被使用,而LastWriteTime能更准确反映文件的实际使用情况。
工具提供了两种目录遍历方式:
通过布尔参数includeSubDir控制,默认值为true(包含子目录)。这种设计既满足了大多数需要递归清理的场景,又为特殊需求提供了灵活性。
文件操作中可能遇到多种异常情况:
代码采用分层异常处理策略:
csharp复制using System;
using System.IO;
public class FileCleaner
{
/// <summary>
/// 清理目录:保留最近指定天数的文件,删除更早的文件
/// </summary>
/// <param name="targetDir">要清理的目录</param>
/// <param name="retentionDays">保留天数,默认30天</param>
/// <param name="includeSubDir">是否包含子文件夹,默认true</param>
public static void CleanFiles(string targetDir, int retentionDays = 30,
bool includeSubDir = true)
{
try
{
// 验证目录存在性
if (!Directory.Exists(targetDir))
{
Console.WriteLine($"[WARN] 目录不存在:{targetDir}");
return;
}
// 计算临界时间点
DateTime limitTime = DateTime.Now.AddDays(-retentionDays);
Console.WriteLine($"[INFO] 保留阈值:{limitTime:yyyy-MM-dd HH:mm:ss}");
// 获取文件列表
var searchOption = includeSubDir ?
SearchOption.AllDirectories : SearchOption.TopDirectoryOnly;
string[] files = Directory.GetFiles(targetDir, "*.*", searchOption);
int deleteCount = 0;
int skipCount = 0;
// 遍历处理每个文件
foreach (string file in files)
{
try
{
FileInfo fi = new FileInfo(file);
// 跳过只读文件
if (fi.IsReadOnly)
{
Console.WriteLine($"[SKIP] 跳过只读文件:{file}");
skipCount++;
continue;
}
// 判断并删除过期文件
if (fi.LastWriteTime < limitTime)
{
fi.Delete();
deleteCount++;
Console.WriteLine($"[DEL] 已删除:{file}");
}
}
catch (Exception ex)
{
Console.WriteLine($"[ERROR] 处理文件失败:{file},原因:{ex.Message}");
}
}
// 输出统计信息
Console.WriteLine($"\n[RESULT] 处理完成!" +
$"\n总文件数:{files.Length}" +
$"\n删除文件:{deleteCount}" +
$"\n跳过文件:{skipCount}" +
$"\n保留文件:{files.Length - deleteCount - skipCount}");
}
catch (Exception ex)
{
Console.WriteLine($"[FATAL] 清理异常:{ex.Message}");
}
}
}
相比初始版本,这个增强实现增加了以下特性:
csharp复制public static void Main()
{
// 基本用法:清理默认30天前的文件,包含子目录
FileCleaner.CleanFiles(@"F:\OvlData\TestResult\Recipes\20\20251212143046");
// 高级用法:只保留15天内的文件,不处理子目录
FileCleaner.CleanFiles(@"D:\Logs", retentionDays: 15, includeSubDir: false);
// 特殊场景:保留90天的备份文件
FileCleaner.CleanFiles(@"\\NAS\Backups", retentionDays: 90);
}
为了使清理工作完全自动化,可以考虑以下集成方式:
Windows任务计划程序集成:
作为Windows服务运行:
ASP.NET Core应用集成:
csharp复制// 在Startup.cs中配置定时任务
public void ConfigureServices(IServiceCollection services)
{
services.AddHostedService<FileCleanupService>();
}
public class FileCleanupService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
FileCleaner.CleanFiles("/var/logs/app");
await Task.Delay(TimeSpan.FromDays(1), stoppingToken);
}
}
}
处理大量文件时,可以考虑以下优化措施:
csharp复制Parallel.ForEach(files, file => {
// 文件处理逻辑
});
csharp复制var files = Directory.EnumerateFiles(targetDir, "*.*", searchOption);
csharp复制if (files.Length > 1000)
{
// 简化日志输出
}
csharp复制var permission = new FileIOPermission(FileIOPermissionAccess.Write, file);
try {
permission.Demand();
} catch {
// 记录权限不足
}
csharp复制public static void DryRun(string targetDir, int retentionDays = 30)
{
// 只列出将要删除的文件,不实际执行删除
}
csharp复制File.Move(filePath, Path.Combine(RecycleBinPath, fileName));
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 文件无法删除 | 文件被占用 | 使用Process Explorer查找占用进程 |
| 权限不足 | 当前用户无修改权限 | 以管理员身份运行程序 |
| 路径过长 | 超过260字符限制 | 启用长路径支持或缩短路径 |
| 文件名含特殊字符 | 编码问题 | 使用Unicode路径处理方式 |
场景: 处理数万文件时速度很慢
优化步骤:
程序生成的日志可以导入到日志分析系统(如ELK)进行进一步分析:
code复制\[(INFO|WARN|ERROR)\].*
powershell复制Get-Content clean.log | Select-String "\[DEL\]" | Group-Object { $_.Line.Split('.')[-1] }
powershell复制Get-Content clean.log | Select-String "\[ERROR\]" | Measure-Object
在某自动化测试平台中,我们集成了这个文件清理工具,配置如下:
实施效果:
一个分布式系统使用增强版清理工具管理日志:
csharp复制// 不同日志类型不同保留策略
FileCleaner.CleanFiles("/logs/debug", retentionDays: 7);
FileCleaner.CleanFiles("/logs/info", retentionDays: 30);
FileCleaner.CleanFiles("/logs/audit", retentionDays: 365);
// 使用JSON配置文件管理策略
var policies = JsonConvert.DeserializeObject<CleanPolicy[]>("clean-policy.json");
foreach (var policy in policies)
{
FileCleaner.CleanFiles(policy.Path, policy.RetentionDays);
}
关键改进: