我试图在Windows Service中将任何内容更新到文本文件时使用FileSystemWatcher读取文本文件。现在我遇到的问题是没有得到我应该放置我的FileSystemWatcher代码的方式以便我会被调用一旦textfile被更改。我需要将其添加到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
}
}
}
答案 0 :(得分:1)
我是否需要将其添加到OnStart()
中
是
但是,没有必要为此目的创建一个线程。设置FileSystemWatcher.EnableRaisingEvents
后,将在线程池中触发事件:您可以从OnStart
返回。
答案 1 :(得分:0)
理查德的答案是正确的。但是,Changed事件至少触发两次,因为默认情况下,FileSystemWatcher在创建文件时触发一次,然后在每次文件系统将其内容刷新到磁盘时再次触发。对于大文件,您可能会由于多次磁盘写入而导致多个Change事件。如果您是第一次尝试打开文件,则Change会在文件写入过程中被锁定或文件内容不完整时触发错误。
我发现的最可靠的方法是,在新文件的第一个Change事件上,以较短的间隔(几秒钟)设置一个计时器,然后在每次事件触发时,将其重置为相同时间文件。然后,在为文件触发最后一个Change事件几秒钟后触发定时器时,您将在计时器自己的Elapsed事件中打开文件。
这需要一些额外的代码和变量:
首先,创建一个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 ...
}