You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

服务中FileSystem Watcher未处理文件及服务启动Watcher实现咨询

看起来你在Windows服务里实现FileSystemWatcher+定时器处理文件的逻辑时遇到了文件未被处理的问题,我来给你梳理下核心问题点并提供完整的可运行方案:

核心问题分析

你当前的代码片段里缺少了几个关键环节:

  • 没有使用线程安全的集合存储待处理文件:FileSystemWatcher的事件是在后台线程触发的,定时器的Elapsed事件也是另一个线程,普通List会引发并发访问异常,导致文件丢失。
  • 没有正确订阅Watcher的文件事件(比如Created/Changed),也没开启事件触发开关。
  • 没有处理文件刚创建时可能被占用的情况,直接处理会报错导致文件未被处理。
完整解决方案代码

下面是补全并优化后的代码,包含所有必要的逻辑:

using System;
using System.Collections.Concurrent;
using System.IO;
using System.Threading;
using System.Timers;
using System.ServiceProcess;

namespace FileWatcherService
{
    public partial class FileProcessorService : ServiceBase
    {
        // 用线程安全的队列存储待处理文件,避免并发问题
        private readonly ConcurrentQueue<string> _pendingFiles = new ConcurrentQueue<string>();
        private FileSystemWatcher _watcher;
        private System.Timers.Timer _processingTimer;
        // 监控路径,可根据配置文件读取,这里示例硬编码
        private readonly string _watchPath = @"C:\YourWatchDirectory";

        public FileProcessorService()
        {
            InitializeComponent();
        }

        protected override void OnStart(string[] args)
        {
            StartFileWatcher();
            InitializeProcessingTimer();
        }

        private void StartFileWatcher()
        {
            _watcher = new FileSystemWatcher
            {
                Path = _watchPath,
                // 监控文件名变化和文件写入完成事件
                NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite,
                // 可根据需求修改过滤规则,比如只处理.txt文件:"*.txt"
                Filter = "*.*",
                // 如果需要监控子目录,设置为true
                IncludeSubdirectories = false
            };

            // 订阅文件创建事件(文件写入完成后会触发)
            _watcher.Created += OnFileCreated;
            // 订阅Watcher错误事件,避免异常导致Watcher停止工作
            _watcher.Error += OnWatcherError;
            // 开启事件触发
            _watcher.EnableRaisingEvents = true;
        }

        private void InitializeProcessingTimer()
        {
            _processingTimer = new System.Timers.Timer(1000); // 1秒间隔
            _processingTimer.Elapsed += ProcessPendingFiles;
            _processingTimer.AutoReset = true; // 每秒重复触发
            _processingTimer.Enabled = true;
        }

        private void OnFileCreated(object sender, FileSystemEventArgs e)
        {
            // 将文件路径加入待处理队列
            _pendingFiles.Enqueue(e.FullPath);
        }

        private void OnWatcherError(object sender, ErrorEventArgs e)
        {
            // 这里可以加入日志记录,比如记录Watcher错误信息
            // Logger.Error($"文件监控器出错: {e.GetException().Message}");
            
            // 可选:重启Watcher恢复监控
            _watcher.EnableRaisingEvents = false;
            _watcher.Dispose();
            StartFileWatcher();
        }

        private void ProcessPendingFiles(object sender, ElapsedEventArgs e)
        {
            // 批量处理队列中的所有待处理文件
            while (_pendingFiles.TryDequeue(out string filePath))
            {
                try
                {
                    // 执行文件处理逻辑
                    HandleFileProcessing(filePath);
                }
                catch (Exception ex)
                {
                    // 记录处理异常,可根据需求决定是否重新加入队列(避免死循环)
                    // Logger.Error($"处理文件 {filePath} 失败: {ex.Message}");
                    
                    // 限制重试次数,比如最多重试2次
                    // if (retryCount < 2)
                    // {
                    //     _pendingFiles.Enqueue(filePath);
                    // }
                }
            }
        }

        private void HandleFileProcessing(string filePath)
        {
            if (!File.Exists(filePath)) return;

            bool isProcessed = false;
            int retryAttempts = 3; // 最多重试3次

            // 处理文件可能被占用的情况,重试访问
            while (!isProcessed && retryAttempts > 0)
            {
                try
                {
                    // 以独占方式打开文件,确保文件已写入完成
                    using (var fileStream = File.Open(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
                    {
                        // 这里替换成你的实际处理逻辑,比如读取文件内容、解析数据等
                        // var fileContent = new StreamReader(fileStream).ReadToEnd();
                        // ProcessContent(fileContent);

                        // 处理完成后,将文件移动到归档目录
                        string archiveDir = Path.Combine(_watchPath, "ProcessedFiles");
                        Directory.CreateDirectory(archiveDir);
                        string archivePath = Path.Combine(archiveDir, Path.GetFileName(filePath));
                        File.Move(filePath, archivePath);

                        isProcessed = true;
                    }
                }
                catch (IOException)
                {
                    retryAttempts--;
                    Thread.Sleep(500); // 等待500毫秒后重试
                }
            }
        }

        protected override void OnStop()
        {
            // 停止定时器
            _processingTimer?.Stop();
            _processingTimer?.Dispose();

            // 关闭文件监控器
            _watcher?.EnableRaisingEvents = false;
            _watcher?.Dispose();

            // 处理队列中剩余的文件,确保服务停止前完成所有待处理任务
            while (_pendingFiles.TryDequeue(out string filePath))
            {
                HandleFileProcessing(filePath);
            }
        }
    }
}
关键注意事项
  • 线程安全集合:必须使用ConcurrentQueue(或ConcurrentBag)来存储待处理文件,避免多线程并发访问时的异常和数据丢失。
  • 文件占用处理:文件刚创建时可能还在被写入进程占用,通过重试机制可以避免IOException,确保文件能被正常处理。
  • Watcher错误恢复:订阅Error事件并实现重启逻辑,防止Watcher因意外异常停止监控。
  • 服务停止清理:在OnStop方法中处理剩余文件,确保所有待处理任务都能完成后再停止服务。

内容的提问来源于stack exchange,提问作者techno

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.26 09:45:31