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 { 12 WatcherStrat(@"C:\test", "*.txt"); 13 //由于是控制臺程序,加個輸入避免主線程執行完畢,看不到監控效果 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 } 用上面的方法會發現,在一次文本文件變化的時候OnChanged事件會觸發兩次,這是因為除了文本內容變化之外還有文件其他的屬性也變化了例如修改時間。 為了解決這問題,也便于項目當中實際使用,寫了下面幾個類來實際使用: 主方法: 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 //由于是控制臺程序,加個輸入避免主線程執行完畢,看不到監控效果 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 /// 構造函數 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方法,但是我的項目中文本的寫入軟件不是我們能控制的,所以只有用處理異常的方法來解決。 該文章在 2023/12/26 23:17:33 編輯過 |
關鍵字查詢
相關文章
正在查詢... |