Files
DCIT/DCIT.Core/Services/TemporaryFileCleanupService.cs
2026-07-13 10:04:36 +08:00

184 lines
5.9 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using DCIT.Core.Models;
namespace DCIT.Core.Services
{
public sealed class TemporaryFileCleanupService
{
private static readonly Regex AtomicTempFilePattern = new Regex(@"\.tmp-[0-9a-fA-F]{32}(?=\.)", RegexOptions.Compiled | RegexOptions.IgnoreCase);
private readonly string _tempRoot;
public TemporaryFileCleanupService()
: this(Path.Combine(Path.GetTempPath(), "DCIT"))
{
}
public TemporaryFileCleanupService(string tempRoot)
{
_tempRoot = string.IsNullOrWhiteSpace(tempRoot)
? Path.Combine(Path.GetTempPath(), "DCIT")
: tempRoot;
}
public TemporaryCleanupResult Cleanup(AppConfig config)
{
var result = new TemporaryCleanupResult();
CleanupDedicatedTempDirectory(Path.Combine(_tempRoot, "processing"), result);
CleanupDedicatedTempDirectory(Path.Combine(_tempRoot, "extract"), result);
var configuredOutputDirectories = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
AddDirectory(configuredOutputDirectories, config == null || config.ReplacePage == null ? null : config.ReplacePage.OutputDirectory);
AddDirectory(configuredOutputDirectories, config == null || config.RestorePage == null ? null : config.RestorePage.RestoreDirectory);
foreach (var directory in configuredOutputDirectories)
{
CleanupAtomicTempFiles(directory, result);
}
return result;
}
private static void AddDirectory(ISet<string> directories, string path)
{
if (string.IsNullOrWhiteSpace(path))
{
return;
}
try
{
directories.Add(Path.GetFullPath(path));
}
catch
{
}
}
private static void CleanupAtomicTempFiles(string rootDirectory, TemporaryCleanupResult result)
{
if (string.IsNullOrWhiteSpace(rootDirectory) || !Directory.Exists(rootDirectory))
{
return;
}
IEnumerable<string> files;
try
{
files = Directory.EnumerateFiles(rootDirectory, "*", SearchOption.AllDirectories)
.Where(path => IsAtomicTempFile(Path.GetFileName(path)));
}
catch (Exception ex)
{
result.Failures.Add("无法扫描目录 " + rootDirectory + ": " + ex.Message);
return;
}
foreach (var file in files)
{
TryDeleteFile(file, result);
}
}
private static bool IsAtomicTempFile(string fileName)
{
return !string.IsNullOrWhiteSpace(fileName) && AtomicTempFilePattern.IsMatch(fileName);
}
private static void CleanupDedicatedTempDirectory(string rootDirectory, TemporaryCleanupResult result)
{
if (string.IsNullOrWhiteSpace(rootDirectory) || !Directory.Exists(rootDirectory))
{
return;
}
IEnumerable<string> files;
try
{
files = Directory.EnumerateFiles(rootDirectory, "*", SearchOption.AllDirectories).ToList();
}
catch (Exception ex)
{
result.Failures.Add("无法扫描目录 " + rootDirectory + ": " + ex.Message);
return;
}
foreach (var file in files)
{
TryDeleteFile(file, result);
}
TryDeleteEmptyDirectories(rootDirectory, result);
}
private static void TryDeleteEmptyDirectories(string rootDirectory, TemporaryCleanupResult result)
{
IEnumerable<string> directories;
try
{
directories = Directory.EnumerateDirectories(rootDirectory, "*", SearchOption.AllDirectories)
.OrderByDescending(path => path.Length)
.ToList();
}
catch (Exception ex)
{
result.Failures.Add("无法枚举临时目录 " + rootDirectory + ": " + ex.Message);
return;
}
foreach (var directory in directories)
{
try
{
if (!Directory.EnumerateFileSystemEntries(directory).Any())
{
Directory.Delete(directory);
}
}
catch (Exception ex)
{
result.Failures.Add("无法删除临时目录 " + directory + ": " + ex.Message);
}
}
try
{
if (!Directory.EnumerateFileSystemEntries(rootDirectory).Any())
{
Directory.Delete(rootDirectory);
}
}
catch (Exception ex)
{
result.Failures.Add("无法删除临时目录 " + rootDirectory + ": " + ex.Message);
}
}
private static void TryDeleteFile(string filePath, TemporaryCleanupResult result)
{
try
{
if (File.Exists(filePath))
{
File.Delete(filePath);
result.RemovedFiles.Add(filePath);
}
}
catch (Exception ex)
{
result.Failures.Add("无法删除临时文件 " + filePath + ": " + ex.Message);
}
}
}
public sealed class TemporaryCleanupResult
{
public IList<string> RemovedFiles { get; } = new List<string>();
public IList<string> Failures { get; } = new List<string>();
}
}