繁体   English   中英

Windows 服务中的 TextFile Reader 和 FileSystemWatcher

[英]TextFile Reader and FileSystemWatcher in Windows Service

我正在尝试使用 FileSystemWatcher 在 Windows 服务中的任何内容更新到文本文件后立即读取文本文件。文本文件已更改。我是否需要将此添加到 Windows 服务的OnStart()方法或其他任何地方。

这是我的代码结构..

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
            }
        }

    }

我是否需要将其添加到 OnStart() 中

是的。

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

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

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

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

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

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

  • 如果不是,那么:
    • 创建一个新的Timer实例
    • 将它的状态对象设置为文件的名称,这样当它的Elapsed事件触发时,你就会知道你应该处理哪个文件(使用闭包和 lambda 函数也可以实现相同的最终结果,但状态对象更简单)
    • 使用文件名作为键将新的计时器实例添加到字典中
  • 如果是(即这不是该文件的第一个 Change 事件):
    • 在字典中查找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 ...
    }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM