[英]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 函数也可以实现相同的最终结果,但状态对象更简单)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.