C#使用文件監(jiān)控對象FileSystemWatcher的幾種方案
當(dāng)前位置:點(diǎn)晴教程→知識管理交流
→『 技術(shù)文檔交流 』
最近在項(xiàng)目中有這么個(gè)需求,就是得去實(shí)時(shí)獲取某個(gè)在無規(guī)律改變的文本文件中的內(nèi)容。首先想到的是用程序定期去訪問這個(gè)文件,因?yàn)閷?shí)時(shí)性要求很高,間隔不能超過1S,而且每次獲取到文本內(nèi)容都要去分發(fā)給web服務(wù)器做別的操作,而那個(gè)文本的寫入有時(shí)候會頻繁,1秒可能多次,但是也有可能在相當(dāng)長一段時(shí)間內(nèi)是沒有任何寫入的。
這樣一來如果每秒都去訪問文件的話,一個(gè)是IO問題,還有就是每次操作都會引起后端一系列程序的反應(yīng),文本在長時(shí)間內(nèi)無寫入的話,一秒一次的觸發(fā)一系列徒勞的事情太不可取了。 最終發(fā)現(xiàn)了c#中的FileSystemWatcher對象,在應(yīng)用FileSystemWatcher之前,首先了解一下這個(gè)對象的基本屬性和事件,首先普及一下FileSystemWatcher基本知識。 FileSystemWatcher基礎(chǔ) 屬性: Path——這個(gè)屬性告訴FileSystemWatcher它需要監(jiān)控哪條路徑。例如,如果我們將這個(gè)屬性設(shè)為“C:\test”,對象就監(jiān)控test目錄下所有文件發(fā)生的所有改變(包括刪除,修改,創(chuàng)建,重命名)。 IncludeSubDirectories——這個(gè)屬性說明FileSystemWatcher對象是否應(yīng)該監(jiān)控子目錄中(所有文件)發(fā)生的改變。 Filter——這個(gè)屬性允許你過濾掉某些類型的文件發(fā)生的變化。例如,如果我們只希望在TXT文件被修改/新建/刪除時(shí)提交通知,可以將這個(gè)屬性設(shè)為“*txt”。在處理高流量或大型目錄時(shí),使用這個(gè)屬性非常方便。 NotifyFilter——獲取或設(shè)置要監(jiān)視的更改類型??梢赃M(jìn)一步的過濾要監(jiān)控的更改類型,如watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName; 事件: Changed——當(dāng)被監(jiān)控的目錄中有一個(gè)文件被修改時(shí),就提交這個(gè)事件。值得注意的是,這個(gè)事件可能會被提交多次,即使文件的內(nèi)容僅僅發(fā)生一項(xiàng)改變。這是由于在保存文件時(shí),文件的其它屬性也發(fā)生了改變。 Created ——當(dāng)被監(jiān)控的目錄新建一個(gè)文件時(shí),就提交這個(gè)事件。如果你計(jì)劃用這個(gè)事件移動新建的事件,你必須在事件處理器中寫入一些錯(cuò)誤處理代碼,它能處理當(dāng)前文件 被其它進(jìn)程使用的情況。之所以要這樣做,是因?yàn)镃reated事件可能在建立文件的進(jìn)程釋放文件之前就被提交。如果你沒有準(zhǔn)備正確處理這種情況的代碼,就 可能出現(xiàn)異常。 Deleted——當(dāng)被監(jiān)控的目錄中有一個(gè)文件被刪除,就提交這個(gè)事件。 Renamed——當(dāng)被監(jiān)控的目錄中有一個(gè)文件被重命名,就提交這個(gè)事件。 注:如果你沒有將EnableRaisingEvents設(shè)為真,系統(tǒng)不會提交任何一個(gè)事件。如果有時(shí)FileSystemWatcher對象似乎無法工作,請首先檢查EnableRaisingEvents,確保它被設(shè)為真。 事件處理
當(dāng) FileSystemWatcher調(diào)用一個(gè)事件處理器時(shí),它包含兩個(gè)自變量——一個(gè)叫做“sender”的對象和一個(gè)叫做“e”的 FileSystemEventArgs對象。我們感興趣的自變量為FileSystemEventArgs自變量。這個(gè)對象中包含有提交事件的原因。以 下是FileSystemEventArgs對象的一些屬性: 屬性:
Name——這個(gè)屬性中使事件被提交的文件的名稱。其中并不包含文件的路徑——只包含使用事件被提交的文件或目錄名稱。 ChangeType——這是一個(gè)WatcherChangeTypes,它指出要提交哪個(gè)類型的事件。其有效值包括: Changed Created Deleted Renamed FullPath——這個(gè)屬性中包含使事件被提交的文件的完整路徑,包括文件名和目錄名。 注 意:FileSystemEventArgs對象是監(jiān)控文件夾下有文件創(chuàng)建、刪除、修改時(shí)的自變量,如果是重命名的話為RenamedEventArgs 對象此時(shí)除了FileSystemEventArgs對象的屬性值,多了一個(gè)OldFullPath,為重命名之前的文件名。 以上為FileSystemEventArgs的基本知識,大部分是從網(wǎng)上搜找的然后自己稍微整理了一下。 下面為簡單用法:
01 using System; 02 using System.IO; 03 04 namespace test 05 { 06 class Program 07 { 08 static void Main(string[] args) 09 { 12 WatcherStrat(@"C:\test", "*.txt"); 13 //由于是控制臺程序,加個(gè)輸入避免主線程執(zhí)行完畢,看不到監(jiān)控效果 14 Console.ReadKey(); 16 } 17 20 private static void WatcherStrat(string path, string filter) 21 { 22 FileSystemWatcher watcher = new FileSystemWatcher(); 23 watcher.Path = path; 25 watcher.Filter = filter; 27 watcher.Changed += new FileSystemEventHandler(OnProcess); 28 watcher.Created += new FileSystemEventHandler(OnProcess); 29 watcher.Deleted += new FileSystemEventHandler(OnProcess); 30 watcher.Renamed += new RenamedEventHandler(OnRenamed); 32 watcher.EnableRaisingEvents = true; 33 } 34 38 private static void OnProcess(object source, FileSystemEventArgs e) 39 { 40 if (e.ChangeType == WatcherChangeTypes.Created) 41 { 42 OnCreated(source, e); 44 } 45 else if (e.ChangeType == WatcherChangeTypes.Changed) 46 { 47 OnChanged(source, e); 49 } 50 else if (e.ChangeType == WatcherChangeTypes.Deleted) 51 { 52 OnDeleted(source, e); 54 } 55 } 56 57 private static void OnCreated(object source, FileSystemEventArgs e) 58 { 60 Console.WriteLine("文件新建事件處理邏輯"); 62 } 63 64 private static void OnChanged(object source, FileSystemEventArgs e) 65 { 67 Console.WriteLine("文件改變事件處理邏輯"); 68 } 69 70 private static void OnDeleted(object source, FileSystemEventArgs e) 71 { 73 Console.WriteLine("文件刪除事件處理邏輯"); 74 } 75 76 private static void OnRenamed(object source, RenamedEventArgs e) 77 { 79 Console.WriteLine("文件重命名事件處理邏輯"); 80 } 82 } 83 } 用上面的方法會發(fā)現(xiàn),在一次文本文件變化的時(shí)候OnChanged事件會觸發(fā)兩次,這是因?yàn)槌宋谋緝?nèi)容變化之外還有文件其他的屬性也變化了例如修改時(shí)間。 為了解決這問題,也便于項(xiàng)目當(dāng)中實(shí)際使用,寫了下面幾個(gè)類來實(shí)際使用: 主方法: 01 using System; 02 using System.IO; 03 04 namespace test 05 { 06 class Program 07 { 08 static void Main(string[] args) 09 { 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 //由于是控制臺程序,加個(gè)輸入避免主線程執(zhí)行完畢,看不到監(jiān)控效果 20 Console.ReadKey(); 22 } 23 24 private static void OnCreated(object source, FileSystemEventArgs e) 25 { 27 Console.WriteLine("文件新建事件處理邏輯"); 29 } 30 31 private static void OnChanged(object source, FileSystemEventArgs e) 32 { 34 Console.WriteLine("文件改變事件處理邏輯"); 35 } 36 37 private static void OnDeleted(object source, FileSystemEventArgs e) 38 { 40 Console.WriteLine("文件刪除事件處理邏輯"); 41 } 42 43 private static void OnRenamed(object source, RenamedEventArgs e) 44 { 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; 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 { 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 /// 構(gòu)造函數(shù) 024 /// </summary> 025 /// <param name="path">要監(jiān)控的路徑</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 // 是否監(jiān)控子目錄 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 /// 開始監(jiān)控 047 /// </summary> 048 public void Start() 049 { 050 fsWather.EnableRaisingEvents = true; 051 } 052 053 /// <summary> 054 /// 停止監(jiān)控 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來處理一次改變觸發(fā)兩次事件的問題,要注意的是在實(shí)際項(xiàng)目使用中,在通過監(jiān)控文件事情觸發(fā)時(shí)開一個(gè)線程 WatcherProcess去處理自己業(yè)務(wù)邏輯的時(shí)候,不管業(yè)務(wù)邏輯成功或者失?。ɡ缬挟惓伋鲆欢ㄒ猼ry一下)一定要讓 WatcherProcess的 Completed也就是MyFileSystemWather的WatcherProcess_OnCompleted執(zhí)行去移除對應(yīng)變化文件的 Hashtable的key,不然下次此文件改變時(shí)是無法觸發(fā)你的業(yè)務(wù)邏輯的。 還有就是在進(jìn)行文件監(jiān)控的時(shí)候, 被監(jiān)控文件在寫入的時(shí)候,是會有I/O沖突的,即使寫入文件是FileShare.Read的也會出現(xiàn),要真正解決貌似只有FileMaping方法,但是我的項(xiàng)目中文本的寫入軟件不是我們能控制的,所以只有用處理異常的方法來解決。 該文章在 2023/12/26 23:17:33 編輯過 |
關(guān)鍵字查詢
相關(guān)文章
正在查詢... |