【问题标题】:TextFile Reader and FileSystemWatcher in Windows ServiceWindows 服务中的 TextFile Reader 和 FileSystemWatcher
【发布时间】:2014-07-25 07:28:25
【问题描述】:

我正在尝试使用 FileSystemWatcher 在任何内容更新到 Windows 服务中的文本文件后立即读取文本文件。现在我面临的问题是没有找到应该放置 FileSystemWatcher 代码的方式,以便我会被调用一旦文本文件被更改。我需要将其添加到OnStart() Windows 服务方法或其他任何地方。

这是我的代码结构..

protected override void OnStart(string[] args)
    {
        _thread = new Thread(startReadingTextFile);
        _thread.Start();
    }

    public void startReadingTextFile() {
        _freader = new AddedContentReader(TextFileLocation);
    }
    private void Watcher_Changed(object sender, FileSystemEventArgs e)
    {
        string addedContent = _freader.GetAddedLines();
    }

请帮助我。谢谢..

更新代码..

 protected override void OnStart(string[] args)
    {
        if (lastLineReadOffset == 0)
        {
            _freader = new AddedContentReader(TextFileLocation);

        }
        //If you have saved the last position when the application did exit then you can use that value here to start from that location like the following
        //_freader = new AddedContentReader("E:\\tmp\\test.txt",lastReadPosition);
        else
        {
            _freader = new AddedContentReader(TextFileLocation, lastLineReadOffset);
        }

        FileSystemWatcher Watcher = new FileSystemWatcher("C:\\temp");
        Watcher.EnableRaisingEvents = true;
        Watcher.Changed += new FileSystemEventHandler(Watcher_Changed);
    }



    private void Watcher_Changed(object sender, FileSystemEventArgs e)
    {
        string addedContent = _freader.GetAddedLines();
        //you can do whatever you want with the lines
        using (StringReader reader = new StringReader(addedContent))
        {
            string line;
            while ((line = reader.ReadLine()) != null)
            {
                // Call the Processing Function
            }
        }

    }

【问题讨论】:

    标签: c# windows-services text-files filesystemwatcher


    【解决方案1】:

    我需要将它添加到 OnStart() 中

    是的。

    但是,没有必要为此创建线程。一旦设置了FileSystemWatcher.EnableRaisingEvents,就会在线程池中触发事件:你可以从OnStart返回。

    【讨论】:

    • 我已经用我的新代码更新了我的帖子。请看看它是否正确或需要任何修改?
    • @user3816352:在启用之前设置事件(否则您可能会错过一些更改:并发会找到一种方法,但间隔时间很短)。
    【解决方案2】:

    理查德的回答是正确的。但是,Changed 事件至少会触发两次,因为默认情况下 FileSystemWatcher 在创建文件时触发一次,然后在每次文件系统将其内容刷新到磁盘时再次触发。对于大文件,您可能会收到由多个磁盘写入引起的多个更改事件。如果您尝试在第一次更改触发时打开文件,如果文件被写入过程锁定或获得不完整的文件内容,则可能会出错。

    我发现的最可靠的方法是在新文件的第一个更改事件上设置一个间隔很短(几秒钟)的计时器,然后在每次事件触发时重置它相同文件。然后,您在计时器自己的 Elapsed 事件中打开该文件,该事件在为文件触发最后一个 Change 事件几秒钟后触发。

    这需要一些额外的代码和变量:

    首先,创建一个Dictionary<string, Timer> 来跟踪每个文件名的计时器。

    在您的 Change 事件处理程序中,您需要检查字典是否已经包含文件名作为键(在 lock 块中以处理线程并发问题)。

    • 如果不是,那么:
      • 创建一个新的Timer 实例
      • 将其状态对象设置为文件名,这样当其Elapsed 事件触发时,您就会知道您应该处理哪个文件(同样的最终结果也可以使用闭包和 lambda 函数来实现,但是状态对象更简单)
      • 使用文件名作为键将新的计时器实例添加到字典中
    • 如果是(即这不是该文件的第一个更改事件):
      • 在字典中查找Timer 实例
      • 重置其间隔以进一步推送其Elapsed 事件

    然后在计时器的Elapsed 事件的处理程序中进行实际处理和清理:

    • 从传入事件参数的定时器状态对象中获取文件名
    • 通过文件名在字典中查找Timer实例并处理它
    • 从字典中删除计时器,即Remove(key),其中key是文件名(上面的三个动作应该发生在lock块内)
    • 打开文件,然后用它做任何你想做的事情。

    以下是您可能希望在服务中实现此逻辑的方式:

        const int DELAY = 2000; // milliseconds
        const WatcherChangeTypes FILE_EVENTS = WatcherChangeTypes.Created | WatcherChangeTypes.Changed | WatcherChangeTypes.Renamed;
    
        FileSystemWatcher _fsw;
        Dictionary<string, Timer> _timers = new Dictionary<string, Timer>();
        object _lock = new object();
    
        public void Start()
        {
            _fsw = new FileSystemWatcher(Directory, FileFilter)
            {
                IncludeSubdirectories = false,
                EnableRaisingEvents = true
            };
            _fsw.Created += OnFileChanged;
            _fsw.Changed += OnFileChanged;
        }
    
        private void OnFileChanged(object sender, FileSystemEventArgs e)
        {
            try
            {
                // When a file is created in the monitored directory, set a timer to process it after a short
                // delay and add the timer to the queue.
                if (FILE_EVENTS.HasFlag(e.ChangeType))
                {
                    lock (_lock)
                    {
                        // File events may fire multiple times as the file is being written to the disk and/or renamed, 
                        // therefore the first time we create a new timer and then reset it on subsequent events so that 
                        // the file is processed shortly after the last event fires.
                        if (_timers.TryGetValue(e.FullPath, out Timer timer))
                        {
                            timer.Change(DELAY, 0);
                        }
                        else
                        {
                            _timers.Add(e.FullPath, new Timer(OnTimerElapsed, e.FullPath, DELAY, 0));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                // handle errors
            }
        }
    
        private void OnTimerElapsed(object state)
        {
            var fileName = (string)state;
            lock (_lock)
            {
                try { _timers[fileName].Dispose(); } catch { }
                try { _timers.Remove(fileName); } catch { }
            }
            // open the file ...
        }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-20
      • 1970-01-01
      • 1970-01-01
      • 2011-03-16
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多