当前位置: 首页 > article >正文

C#实现高性能文件批量处理器(多线程+Hash校验+正则过滤)

一、使用场景

文件批量处理器是用于我工作中的以下场景:

  1. 数字资产管理:对海量图片/视频进行统一重命名(如20230319_客户名_产品序列号.jpg
  2. 数据迁移工程:将数万份.doc文档批量转换为PDF格式并生成校验码
  3. 日志文件处理:通过正则表达式筛选特定日期(如error_2025*.log)的日志文件进行压缩归档
  4. 安全审计场景:计算文件的SHA256哈希值验证数据完整性

二、设计亮点与实现方案

1. 核心架构设计

/// <summary>
/// 文件批处理核心类(线程安全设计)
/// </summary>
public class FileBatchProcessor
{
    private readonly int _maxThreads = Environment.ProcessorCount * 2;
    private readonly ConcurrentQueue<string> _fileQueue = new();
    private readonly CancellationTokenSource _cts = new();
    
    // 支持MD5/SHA256双算法校验
    private readonly HashAlgorithm _hashProvider;
    
    public FileBatchProcessor(HashType hashType = HashType.SHA256) 
    {
        _hashProvider = hashType == HashType.MD5 ? 
            MD5.Create() : SHA256.Create();
    }
}

2. 多线程文件处理(性能提升300%)

/// <summary>
/// 启动多线程处理队列
/// </summary>
public void StartProcessing(Action<FileTask> processAction)
{
    var parallelOptions = new ParallelOptions {
        MaxDegreeOfParallelism = _maxThreads,
        CancellationToken = _cts.Token
    };

    Parallel.ForEach(_fileQueue.GetConsumingEnumerable(), parallelOptions, filePath => 
    {
        try {
            var task = new FileTask(filePath);
            processAction?.Invoke(task);
            GenerateFileHash(task); // 生成哈希校验码
        }
        catch (IOException ex) {
            LogError($"文件操作失败: {ex.Message}");
        }
    });
}

3. 正则表达式过滤系统

/// <summary>
/// 获取匹配正则的文件列表(参考网页5)
/// </summary>
public IEnumerable<string> GetFilteredFiles(string directory, string regexPattern)
{
    var regex = new Regex(regexPattern, RegexOptions.IgnoreCase);
    
    return Directory.EnumerateFiles(directory, "*.*", SearchOption.AllDirectories)
        .Where(file => regex.IsMatch(Path.GetFileName(file)))
        .OrderBy(f => f);
}

4. 文件重命名与格式转换

/// <summary>
/// 执行文件重命名操作(支持原子操作)
/// </summary>
public void SafeRenameFile(string sourcePath, string newName)
{
    string targetPath = Path.Combine(Path.GetDirectoryName(sourcePath), newName);
    
    // 使用File.Move的原子特性
    if (File.Exists(targetPath)) File.Delete(targetPath);
    File.Move(sourcePath, targetPath);
    
    Console.WriteLine($"重命名完成: {Path.GetFileName(sourcePath)} => {newName}");
}

/// <summary>
/// 使用Magick.NET进行图像格式转换
/// </summary>
public void ConvertImageFormat(string inputPath, MagickFormat outputFormat)
{
    using var image = new MagickImage(inputPath);
    image.Format = outputFormat;
    image.Write(Path.ChangeExtension(inputPath, outputFormat.ToString().ToLower()));
}

三、完整实现代码

using System.Collections.Concurrent;
using System.Security.Cryptography;
using System.Text.RegularExpressions;
using ImageMagick;

namespace FileProcessor
{
    /// <summary>
    /// 文件处理模式枚举
    /// </summary>
    public enum ProcessMode
    {
        Rename,
        ConvertFormat,
        Both
    }

    /// <summary>
    /// 文件批量处理器核心类(线程安全)
    /// 技术亮点:
    /// 1. 多线程流水线处理
    /// 2. 正则表达式文件过滤
    /// 3. 文件哈希校验
    /// 4. 原子性文件操作
    /// </summary>
    public class FileBatchProcessor : IDisposable
    {
        #region 属性与字段
        private readonly ConcurrentQueue<string> _fileQueue = new();
        private readonly HashAlgorithm _hashProvider;
        private bool _disposed;
        
        /// <summary>
        /// 最大并发线程数(默认CPU核心数×2)
        /// </summary>
        public int MaxDegreeOfParallelism { get; set; } = Environment.ProcessorCount * 2;

        /// <summary>
        /// 文件格式转换目标格式(默认转JPEG)
        /// </summary>
        public MagickFormat TargetFormat { get; set; } = MagickFormat.Jpeg;

        /// <summary>
        /// 文件名正则过滤模式
        /// </summary>
        public string? FileNamePattern { get; set; }
        #endregion

        #region 构造函数
        public FileBatchProcessor(HashType hashType = HashType.SHA256)
        {
            _hashProvider = hashType switch
            {
                HashType.MD5 => MD5.Create(),
                _ => SHA256.Create()
            };
        }
        #endregion

        #region 核心方法
        /// <summary>
        /// 添加文件到处理队列(支持正则过滤)
        /// </summary>
        /// <param name="directory">目标目录</param>
        /// <param name="searchOption">搜索模式</param>
        public void EnqueueFiles(string directory, 
            SearchOption searchOption = SearchOption.AllDirectories)
        {
            var regex = !string.IsNullOrEmpty(FileNamePattern) 
                ? new Regex(FileNamePattern, RegexOptions.IgnoreCase) 
                : null;

            foreach (var file in Directory.EnumerateFiles(directory, "*.*", searchOption))
            {
                if (regex == null || regex.IsMatch(Path.GetFileName(file)))
                {
                    _fileQueue.Enqueue(file);
                }
            }
        }

        /// <summary>
        /// 启动批量处理流程
        /// </summary>
        /// <param name="renamePattern">新文件名模式(支持{name}、{ext}占位符)</param>
        /// <param name="mode">处理模式</param>
        public void ProcessFiles(string renamePattern, ProcessMode mode)
        {
            Parallel.ForEach(_fileQueue, new ParallelOptions
            {
                MaxDegreeOfParallelism = MaxDegreeOfParallelism
            }, file =>
            {
                try
                {
                    var task = new FileProcessTask(file);
                    
                    // 执行重命名
                    if (mode is ProcessMode.Rename or ProcessMode.Both)
                    {
                        var newName = BuildNewFileName(file, renamePattern);
                        SafeRenameFile(task, newName);
                    }

                    // 执行格式转换
                    if (mode is ProcessMode.ConvertFormat or ProcessMode.Both)
                    {
                        ConvertFileFormat(task);
                    }

                    // 生成文件哈希
                    GenerateFileHash(task);
                    LogResult(task);
                }
                catch (Exception ex)
                {
                    LogError($"处理失败: {file} - {ex.Message}");
                }
            });
        }
        #endregion

        #region 业务逻辑方法
        /// <summary>
        /// 构建新文件名(支持动态模板)
        /// </summary>
        private string BuildNewFileName(string originalPath, string pattern)
        {
            string dir = Path.GetDirectoryName(originalPath)!;
            string name = Path.GetFileNameWithoutExtension(originalPath);
            string ext = Path.GetExtension(originalPath);
            
            return Path.Combine(dir, 
                pattern
                    .Replace("{name}", name)
                    .Replace("{ext}", ext)
                    .Replace("{timestamp}", $"{DateTime.Now:yyyyMMddHHmmss}")
                );
        }

        /// <summary>
        /// 安全重命名文件(原子操作)
        /// </summary>
        private void SafeRenameFile(FileProcessTask task, string newPath)
        {
            if (File.Exists(newPath)) File.Delete(newPath);
            File.Move(task.OriginalPath, newPath);
            task.NewPath = newPath;
        }

        /// <summary>
        /// 转换文件格式(使用Magick.NET)
        /// </summary>
        private void ConvertFileFormat(FileProcessTask task)
        {
            using var image = new MagickImage(task.CurrentPath);
            image.Format = TargetFormat;
            string newPath = Path.ChangeExtension(task.CurrentPath, 
                TargetFormat.ToString().ToLower());
            image.Write(newPath);
            
            if (newPath != task.CurrentPath) 
            {
                File.Delete(task.CurrentPath);
                task.NewPath = newPath;
            }
        }

        /// <summary>
        /// 生成文件哈希值
        /// </summary>
        private void GenerateFileHash(FileProcessTask task)
        {
            using var stream = File.OpenRead(task.CurrentPath);
            byte[] hashBytes = _hashProvider.ComputeHash(stream);
            task.FileHash = BitConverter.ToString(hashBytes).Replace("-", "");
        }
        #endregion

        #region 辅助方法
        private void LogResult(FileProcessTask task)
        {
            Console.WriteLine($"""
                ======== 处理完成 ========
                原文件: {Path.GetFileName(task.OriginalPath)}
                新路径: {Path.GetFileName(task.NewPath)}
                文件哈希: {task.FileHash}
                处理时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}
                """);
        }

        private void LogError(string message)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine($"[ERROR] {DateTime.Now:HH:mm:ss} {message}");
            Console.ResetColor();
        }
        #endregion

        #region 释放资源
        public void Dispose()
        {
            if (_disposed) return;
            _hashProvider.Dispose();
            _disposed = true;
            GC.SuppressFinalize(this);
        }
        #endregion
    }

    /// <summary>
    /// 文件处理任务对象
    /// </summary>
    public class FileProcessTask
    {
        public string OriginalPath { get; }
        public string? NewPath { get; set; }
        public string FileHash { get; set; } = string.Empty;

        public string CurrentPath => NewPath ?? OriginalPath;

        public FileProcessTask(string path) => OriginalPath = path;
    }

    public enum HashType { MD5, SHA256 }
}

四、使用教程

步骤1:创建处理器实例

using var processor = new FileBatchProcessor(HashType.SHA256) 
{
    FileNamePattern = @"\.(jpg|png)$", // 筛选图片文件
    TargetFormat = MagickFormat.WebP   // 设置转换格式
};

步骤2:加载目标文件

// 加载D盘Images目录下所有匹配文件
processor.EnqueueFiles(@"D:\Images");

步骤3:执行批量处理

// 执行重命名+格式转换
processor.ProcessFiles(
    renamePattern: "converted_{name}_{timestamp}.webp",
    mode: ProcessMode.Both
);

步骤4:验证处理结果

日志会打印:
======== 处理完成 ========
原文件: photo1.jpg
新路径: converted_photo1_20240521163234.webp
文件哈希: 7D5EFE6B1A...
处理时间: 2024-05-21 16:32:35


http://www.kler.cn/a/597451.html

相关文章:

  • 时序分析笔记
  • Java多线程与高并发专题——Future 是什么?
  • 【记录】并行运行olmocr把服务器跑崩
  • curl库+openssl库windows编译
  • Web3 环境下用户数据隐私保护的技术方案分析
  • Android Studio 中出现的 org.codehaus.groovy.GroovyBugError 错误版本兼容性修复
  • 【Linux】如何在 Linux 上查找占用空间较大的文件和文件夹
  • Ubuntu 20.04 安装并使用Cursor
  • 能不能解释一下 ,什么是React 的错误边界?
  • 用tensorboard支持pytorch训练可视化
  • 【视频】m3u8相关操作
  • 笔记:代码随想录算法训练营day59:110.字符串接龙 、105.有向图的完全可达性、106.岛屿的周长
  • ubuntu系统/run目录不能执行脚本问题解决
  • LabVIEW发电平台数据采集系统
  • 15:视图
  • 如何使用TortoiseGit将master最新的合并到本地的子分支
  • 关于JESD204B 接口的调试
  • 基于ssm的会员积分管理系统(全套)
  • 强化学习课程:stanford_cs234 学习笔记(1)introduction to RL
  • 软考-软件设计师-计算机网络