在C#使用文件监控对象FileSystemWatcher的几种方案

最近在项目中有这么个需求,就是得去实时获取某个在无规律改变的文本文件中的内容。首先想到的是用程序定期去访问这个文件,因为对实时性要求很高,间隔不能超过1S,而且每次获取到文本内容都要去分发给web服务器做别的操作,而那个文本的写入有时候会频繁,1秒可能多次,但是也有可能在相当长一段时间内是没有任何写入的。

这样一来如果每秒都去访问文件的话,一个是IO问题,还有就是每次操作都会引起后端一系列程序的反应,文本在长时间内无写入的话,一秒一次的触发一系列徒劳的事情太不可取了。

最终发现了c#中的FileSystemWatcher对象,在应用FileSystemWatcher之前,首先了解一下这个对象的基本属性和事件,首先普及一下FileSystemWatcher基本知识。

FileSystemWatcher基础

属性:

Path——这个属性告诉FileSystemWatcher它需要监控哪条路径。例如,如果我们将这个属性设为“C:\test”,对象就监控test目录下所有文件发生的所有改变(包括删除,修改,创建,重命名)。

IncludeSubDirectories——这个属性说明FileSystemWatcher对象是否应该监控子目录中(所有文件)发生的改变。

Filter——这个属性允许你过滤掉某些类型的文件发生的变化。例如,如果我们只希望在TXT文件被修改/新建/删除时提交通知,可以将这个属性设为“*txt”。在处理高流量或大型目录时,使用这个属性非常方便。

NotifyFilter——获取或设置要监视的更改类型。可以进一步的过滤要监控的更改类型,如watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite

| NotifyFilters.FileName | NotifyFilters.DirectoryName;

事件:

Changed——当被监控的目录中有一个文件被修改时,就提交这个事件。值得注意的是,这个事件可能会被提交多次,即使文件的内容仅仅发生一项改变。这是由于在保存文件时,文件的其它属性也发生了改变。

Created
——当被监控的目录新建一个文件时,就提交这个事件。如果你计划用这个事件移动新建的事件,你必须在事件处理器中写入一些错误处理代码,它能处理当前文件
被其它进程使用的情况。之所以要这样做,是因为Created事件可能在建立文件的进程释放文件之前就被提交。如果你没有准备正确处理这种情况的代码,就
可能出现异常。

Deleted——当被监控的目录中有一个文件被删除,就提交这个事件。

Renamed——当被监控的目录中有一个文件被重命名,就提交这个事件。

注:如果你没有将EnableRaisingEvents设为真,系统不会提交任何一个事件。如果有时FileSystemWatcher对象似乎无法工作,请首先检查EnableRaisingEvents,确保它被设为真。

事件处理

FileSystemWatcher调用一个事件处理器时,它包含两个自变量——一个叫做“sender”的对象和一个叫做“e”的
FileSystemEventArgs对象。我们感兴趣的自变量为FileSystemEventArgs自变量。这个对象中包含有提交事件的原因。以
下是FileSystemEventArgs对象的一些属性:

属性:
Name——这个属性中使事件被提交的文件的名称。其中并不包含文件的路径——只包含使用事件被提交的文件或目录名称。

ChangeType——这是一个WatcherChangeTypes,它指出要提交哪个类型的事件。其有效值包括:

Changed

Created

Deleted

Renamed

FullPath——这个属性中包含使事件被提交的文件的完整路径,包括文件名和目录名。


意:FileSystemEventArgs对象是监控文件夹下有文件创建、删除、修改时的自变量,如果是重命名的话为RenamedEventArgs
对象此时除了FileSystemEventArgs对象的属性值,多了一个OldFullPath,为重命名之前的文件名。

以上为FileSystemEventArgs的基本知识,大部分是从网上搜找的然后自己稍微整理了一下。

下面为简单用法:
01 using System;

02 using System.IO;

03

04 namespace test

05 {

06 class Program

07 {

08 static void Main(string[] args)

09 {

10

11

12 WatcherStrat(@"C:\test", "*.txt");

13 //由于是控制台程序,加个输入避免主线程执行完毕,看不到监控效果

14 Console.ReadKey();

15

16 }

17

18

19

20 private static void WatcherStrat(string path, string filter)

21 {

22 FileSystemWatcher watcher = new FileSystemWatcher();

23 watcher.Path = path;

24

25 watcher.Filter = filter;

26

27 watcher.Changed += new FileSystemEventHandler(OnProcess);

28 watcher.Created += new FileSystemEventHandler(OnProcess);

29 watcher.Deleted += new FileSystemEventHandler(OnProcess);

30 watcher.Renamed += new RenamedEventHandler(OnRenamed);

31

32 watcher.EnableRaisingEvents = true;

33 }

34

35

36

37

38 private static void OnProcess(object source, FileSystemEventArgs e)

39 {

40 if (e.ChangeType == WatcherChangeTypes.Created)

41 {

42 OnCreated(source, e);

43

44 }

45 else if (e.ChangeType == WatcherChangeTypes.Changed)

46 {

47 OnChanged(source, e);

48

49 }

50 else if (e.ChangeType == WatcherChangeTypes.Deleted)

51 {

52 OnDeleted(source, e);

53

54 }

55 }

56

57 private static void OnCreated(object source, FileSystemEventArgs e)

58 {

59

60 Console.WriteLine("文件新建事件处理逻辑");

61

62 }

63

64 private static void OnChanged(object source, FileSystemEventArgs e)

65 {

66

67 Console.WriteLine("文件改变事件处理逻辑");

68 }

69

70 private static void OnDeleted(object source, FileSystemEventArgs e)

71 {

72

73 Console.WriteLine("文件删除事件处理逻辑");

74 }

75

76 private static void OnRenamed(object source, RenamedEventArgs e)

77 {

78

79 Console.WriteLine("文件重命名事件处理逻辑");

80 }

81

82 }

83 }

用上面的方法会发现,在一次文本文件变化的时候OnChanged事件会触发两次,这是因为除了文本内容变化之外还有文件其他的属性也变化了例如修改时间。

为了解决这问题,也便于项目当中实际使用,写了下面几个类来实际使用:

主方法:
01 using System;

02 using System.IO;

03

04 namespace test

05 {

06 class Program

07 {

08 static void Main(string[] args)

09 {

10

11

12

13 MyFileSystemWather myWather = new MyFileSystemWather(@"C:\test", "*.txt");

14 myWather.OnChanged += new FileSystemEventHandler(OnChanged);

15 myWather.OnCreated += new FileSystemEventHandler(OnCreated);

16 myWather.OnRenamed += new RenamedEventHandler(OnRenamed);

17 myWather.OnDeleted += new FileSystemEventHandler(OnDeleted);

18 myWather.Start();

19 //由于是控制台程序,加个输入避免主线程执行完毕,看不到监控效果

20 Console.ReadKey();

21

22 }

23

24 private static void OnCreated(object source, FileSystemEventArgs e)

25 {

26

27 Console.WriteLine("文件新建事件处理逻辑");

28

29 }

30

31 private static void OnChanged(object source, FileSystemEventArgs e)

32 {

33

34 Console.WriteLine("文件改变事件处理逻辑");

35 }

36

37 private static void OnDeleted(object source, FileSystemEventArgs e)

38 {

39

40 Console.WriteLine("文件删除事件处理逻辑");

41 }

42

43 private static void OnRenamed(object source, RenamedEventArgs e)

44 {

45

46 Console.WriteLine("文件重命名事件处理逻辑");

47 }

48

49 }

50 }

WatcherProcess类:
01 using System.IO;

02

03 namespace test

04 {

05 public class WatcherProcess

06 {

07 private object sender;

08 private object eParam;

09

10 public event RenamedEventHandler OnRenamed;

11 public event FileSystemEventHandler OnChanged;

12 public event FileSystemEventHandler OnCreated;

13 public event FileSystemEventHandler OnDeleted;

14 public event Completed OnCompleted;

15

16 public WatcherProcess(object sender, object eParam)

17 {

18 this.sender = sender;

19 this.eParam = eParam;

20 }

21

22 public void Process()

23 {

24 if (eParam.GetType() == typeof(RenamedEventArgs))

25 {

26 OnRenamed(sender, (RenamedEventArgs)eParam);

27 OnCompleted(((RenamedEventArgs)eParam).FullPath);

28 }

29 else

30 {

31 FileSystemEventArgs e = (FileSystemEventArgs)eParam;

32 if (e.ChangeType == WatcherChangeTypes.Created)

33 {

34 OnCreated(sender, e);

35 OnCompleted(e.FullPath);

36 }

37 else if (e.ChangeType == WatcherChangeTypes.Changed)

38 {

39 OnChanged(sender, e);

40 OnCompleted(e.FullPath);

41 }

42 else if (e.ChangeType == WatcherChangeTypes.Deleted)

43 {

44 OnDeleted(sender, e);

45 OnCompleted(e.FullPath);

46 }

47 else

48 {

49 OnCompleted(e.FullPath);

50 }

51 }

52 }

53 }

54 }

MyFileSystemWather类:
001 using System;

002 using System.Collections;

003 using System.IO;

004 using System.Threading;

005

006 namespace test

007 {

008

009 public delegate void Completed(string key);

010

011 public class MyFileSystemWather

012 {

013 private FileSystemWatcher fsWather;

014

015 private Hashtable hstbWather;

016

017 public event RenamedEventHandler OnRenamed;

018 public event FileSystemEventHandler OnChanged;

019 public event FileSystemEventHandler OnCreated;

020 public event FileSystemEventHandler OnDeleted;

021

022 /// <summary>

023 /// 构造函数

024 /// </summary>

025 /// <param name="path">要监控的路径</param>

026 public MyFileSystemWather(string path, string filter)

027 {

028 if (!Directory.Exists(path))

029 {

030 throw new Exception("找不到路径:" + path);

031 }

032

033 hstbWather = new Hashtable();

034

035 fsWather = new FileSystemWatcher(path);

036 // 是否监控子目录

037 fsWather.IncludeSubdirectories = false;

038 fsWather.Filter = filter;

039 fsWather.Renamed += new RenamedEventHandler(fsWather_Renamed);

040 fsWather.Changed += new FileSystemEventHandler(fsWather_Changed);

041 fsWather.Created += new FileSystemEventHandler(fsWather_Created);

042 fsWather.Deleted += new FileSystemEventHandler(fsWather_Deleted);

043 }

044

045 /// <summary>

046 /// 开始监控

047 /// </summary>

048 public void Start()

049 {

050 fsWather.EnableRaisingEvents = true;

051 }

052

053 /// <summary>

054 /// 停止监控

055 /// </summary>

056 public void Stop()

057 {

058 fsWather.EnableRaisingEvents = false;

059 }

060

061 /// <summary>

062 /// filesystemWatcher 本身的事件通知处理过程

063 /// </summary>

064 /// <param name="sender"></param>

065 /// <param name="e"></param>

066 private void fsWather_Renamed(object sender, RenamedEventArgs e)

067 {

068 lock (hstbWather)

069 {

070 hstbWather.Add(e.FullPath, e);

071 }

072

073 WatcherProcess watcherProcess = new WatcherProcess(sender, e);

074 watcherProcess.OnCompleted += new Completed(WatcherProcess_OnCompleted);

075 watcherProcess.OnRenamed += new RenamedEventHandler(WatcherProcess_OnRenamed);

076 Thread thread = new Thread(watcherProcess.Process);

077 thread.Start();

078 }

079

080 private void WatcherProcess_OnRenamed(object sender, RenamedEventArgs e)

081 {

082 OnRenamed(sender, e);

083 }

084

085 private void fsWather_Created(object sender, FileSystemEventArgs e)

086 {

087 lock (hstbWather)

088 {

089 hstbWather.Add(e.FullPath, e);

090 }

091 WatcherProcess watcherProcess = new WatcherProcess(sender, e);

092 watcherProcess.OnCompleted += new Completed(WatcherProcess_OnCompleted);

093 watcherProcess.OnCreated += new FileSystemEventHandler(WatcherProcess_OnCreated);

094 Thread threadDeal = new Thread(watcherProcess.Process);

095 threadDeal.Start();

096 }

097

098 private void WatcherProcess_OnCreated(object sender, FileSystemEventArgs e)

099 {

100 OnCreated(sender, e);

101 }

102

103 private void fsWather_Deleted(object sender, FileSystemEventArgs e)

104 {

105 lock (hstbWather)

106 {

107 hstbWather.Add(e.FullPath, e);

108 }

109 WatcherProcess watcherProcess = new WatcherProcess(sender, e);

110 watcherProcess.OnCompleted += new Completed(WatcherProcess_OnCompleted);

111 watcherProcess.OnDeleted += new FileSystemEventHandler(WatcherProcess_OnDeleted);

112 Thread tdDeal = new Thread(watcherProcess.Process);

113 tdDeal.Start();

114 }

115

116 private void WatcherProcess_OnDeleted(object sender, FileSystemEventArgs e)

117 {

118 OnDeleted(sender, e);

119 }

120

121 private void fsWather_Changed(object sender, FileSystemEventArgs e)

122 {

123 if (e.ChangeType == WatcherChangeTypes.Changed)

124 {

125 if (hstbWather.ContainsKey(e.FullPath))

126 {

127 WatcherChangeTypes oldType = ((FileSystemEventArgs)hstbWather[e.FullPath]).ChangeType;

128 if (oldType == WatcherChangeTypes.Created || oldType == WatcherChangeTypes.Changed)

129 {

130 return;

131 }

132 }

133 }

134

135 lock (hstbWather)

136 {

137 hstbWather.Add(e.FullPath, e);

138 }

139 WatcherProcess watcherProcess = new WatcherProcess(sender, e);

140 watcherProcess.OnCompleted += new Completed(WatcherProcess_OnCompleted);

141 watcherProcess.OnChanged += new FileSystemEventHandler(WatcherProcess_OnChanged);

142 Thread thread = new Thread(watcherProcess.Process);

143 thread.Start();

144 }

145

146 private void WatcherProcess_OnChanged(object sender, FileSystemEventArgs e)

147 {

148 OnChanged(sender, e);

149 }

150

151 public void WatcherProcess_OnCompleted(string key)

152 {

153 lock (hstbWather)

154 {

155 hstbWather.Remove(key);

156 }

157 }

158 }

159 }

使
用了线程安全的Hashtable来处理一次改变触发两次事件的问题,要注意的是在实际项目使用中,在通过监控文件事情触发时开一个线程
WatcherProcess去处理自己业务逻辑的时候,不管业务逻辑成功或者失败(例如有异常抛出一定要try一下)一定要让
WatcherProcess的
Completed也就是MyFileSystemWather的WatcherProcess_OnCompleted执行去移除对应变化文件的
Hashtable的key,不然下次此文件改变时是无法触发你的业务逻辑的。

还有就是在进行文件监控的时候, 被监控文件在写入的时候,是会有I/O冲突的,即使写入文件是FileShare.Read的也会出现,要真正解决貌似只有FileMaping方法,但是我的项目中文本的写入软件不是我们能控制的,所以只有用处理异常的方法来解决。

时间: 2024-10-21 10:40:43

在C#使用文件监控对象FileSystemWatcher的几种方案的相关文章

文件监控之FileSystemWatcher(c++)

为了监控web程序的静态文件是否被恶意改动,所以学习了一下FileSystemWatcher  类对文件的监控,由于还在初级阶段,这里只贴一下关于FileSystemWatcher学习的一些代码. 具体代码如下: #using <System.dll> #include <iostream> using namespace std; using namespace System; using namespace System::IO; using namespace System:

Android文件监控FileObserver介绍

在前面的Linux文件系统Inotify机制中介绍了Linux对文件变更监控过程.Android系统在此基础上封装了一个FileObserver类来方便使用Inotify机制.FileObserver是一个抽象类,需要定义子类实现该类的onEvent抽象方法,当被监控的文件或者目录发生变更事件时,将回调FileObserver的onEvent()函数来处理文件或目录的变更事件. 事件监控过程 在FileObserver类中定义了一个静态内部类ObserverThread,该线程类才是真正实现文件

增加cacti监控对象时导致数据库出问题

生产环境: 数据库:mysql 5.5.49-cll-lve 问题:在众多Cacti监控的对象中,由于增加了监控对象nginx,导致cacti在监控所有对象中仅有1个对象出图出数据. 分析: 1.systemutilities –>Rebuild poller cache #需要重建cache吗?2.系统时间不准确,重新设置时间  #需要更新服务器时间吗?3.图像没有生成,运行 # /usr/bin/php /var/www/html/cacti/poller.php --force #出现数据

基于SSM框架下的JAVA文件监控管理系统

每天记录学习,每天会有好心情.*^_^* 今日思考,完成一个文件监控管理系统项目,需要实现哪些功能?此类项目常见描述如下:随着信息技术突飞猛进,网络传输已经成为信息传递的主要方式,信息极端主义的手段越来越高深莫测,重要信息的监控显得十分必要.针对信息安全隐患问题,本章设计了文件监控系统,来实现对传输的重要文件进行实时监控,达到安全传输文件以防非法窃取的目的.SSM(MYECLIPSE)框架及其适合本类项目,使用MVC的思想可以极大程度减少重复工作量.和SSM(MYECLIPSE)框架最配的开发工

增加cacti监控对象时导致无数据无图形出现

生产环境: 操作系统:CentOS release 6.8 (Final) 数据库:mysql 5.5.49-cll-lve 问题:在众多Cacti监控的对象中,由于增加一台服务器增加了一个了监控对象Windows系统,导致cacti在监控所有对象中仅有1个对象出图出数据. 分析: 1.图像没有生成,运行# /usr/bin/php /var/www/html/cacti/poller.php --force #出现数据了吗?2.权限问题,修改权限#chmod 777 -R /var/www/h

C++学习47 文件的概念 文件流类与文件流对象 文件的打开与关闭

迄今为止,我们讨论的输入输出是以系统指定的标准设备(输入设备为键盘,输出设备为显示器)为对象的.在实际应用中,常以磁盘文件作为对象.即从磁盘文件读取数据,将数据输出到磁盘文件.磁盘是计算机的外部存储器,它能够长期保留信息,能读能写,可以刷新重写,方便携带,因而得到广泛使用. 文件(file)是程序设计中一个重要的概念.所谓“文件”,一般指存储在外部介质上数据的集合.一批数据是以文件的形式存放在外部介质(如磁盘.光盘和U盘)上的.操 作系统是以文件为单位对数据进行管理的,也就是说,如果想找存在外部

将插座变量(IBOutlet)关联到*.xib文件中对象 + 将对*.xib对象的操作关联到动作方法(IBAction)

将插座变量(IBOutlet)关联到*.xib文件中对象 以BNRDetailViewController.m和BNRDetailViewController.xib为例(<iOS编程>第10章例子) 1.打开BNRDetailViewController.xib,添加一个UITextField对象: 2.在辅助编辑器中打开BNRDetailViewController.m,方法是:按住Option键并点击项目导航面板中的BNRDetailViewController.m: 3.按住Contr

C++文件流类与文件流对象具体介绍

文件流是以外存文件为输入输出对象的数据流.输出文件流是从内存流向外存文件的数据,输入文件流是从外存文件流向内存的数据.每一个文件流都有一个内存缓冲区与之对应. 请区分文件流与文件的概念,不用误以为文件流是由若干个文件组成的流.文件流本身不是文件,而只是以文件为输入输出对象的流.若要对磁盘文件输入输出,就必须通过文件流来实现. 在C++的I/O类库中定义了几种文件类,专门用于对磁盘文件的输入输出操作.在 图13.2(详情请查看:与C++输入输出有关的类和对象)中可以看到除了标准输入输出流类istr

nrpe监控对象和阀值部分注解

nrpe监控对象和阀值: 监控对象 监控阀值 主 机 资 源 主机存活: check_ping -w 3000.0,80% -c 5000.0,100% -p 5(3000毫秒响应时间内, 丢包率超过80%报警告,5000毫秒响应时间内,丢包率超过 100%报危急,一共发送5个包) 登录用户: check_user -w 5 -c 10(w为警告,c为危急) 系统负载: check_load -w 15,10,5 -c 30,25,20(1分钟,5分钟,15分钟大于对应 的等待进程数则警告或危急