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(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 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 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 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 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 RemovedFiles { get; } = new List(); public IList Failures { get; } = new List(); } }