因為經常要寫各種設定檔,所以曾經寫過監控檔案變更的方法,後來其他地方要用到時每次都要copy不方便,於是修改了下代碼,封裝成一個類庫,這樣只要需要監控的檔案都可以通過這個類庫來實現監控,以下是代碼
Observer.cs,檔案監控類,利用微軟的FileSystemWatcher 實現即時監控,只監控修改
/// <summary> /// 檔案觀察者,只監控檔案內容修改,不監控其他諸如檔案名稱被改,檔案被刪等 /// </summary> public sealed class Observer { /// <summary> /// 要通知的對象集合 /// </summary> private List<Watcher> _watchers; /// <summary> /// 檔案夾監視者 /// </summary> private FileSystemWatcher _fsw; /// <summary> /// 當前監控的檔案夾完整路徑 /// </summary> public string FullPath { get { return this._fsw.Path; } } /// <summary> /// 檔案觀察者,只監控檔案內容修改,不監控其他諸如檔案名稱被改,檔案被刪等 /// </summary> public Observer() : this(false) { } public Observer(bool includeSubdirectories) : this(AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "Config", "*Config.xml", includeSubdirectories) { } public Observer(string folderPhyPath, string fileFiler) : this(folderPhyPath, fileFiler, false) { } /// <summary> /// 檔案觀察者,只監控檔案內容修改,不監控其他諸如檔案名稱被改,檔案被刪等 /// </summary> /// <param name="folderPhyPath">要監控的檔案夾實體路徑,預設為當前該程式的根目錄下的Config檔案夾</param> /// <param name="fileFiler">擷取或設定篩選字串,用於確定在目錄中監視哪些檔案,預設*Config.xml</param> /// <param name="includeSubdirectories">是否監控子檔案夾,預設false</param> public Observer(string folderPhyPath, string fileFiler, bool includeSubdirectories) { this._watchers = new List<Watcher>(); _fsw = new FileSystemWatcher(); _fsw.Path = folderPhyPath.ToStandardPath(); _fsw.NotifyFilter = NotifyFilters.LastWrite; _fsw.Filter = fileFiler; _fsw.Changed += new FileSystemEventHandler(fsw_Changed); _fsw.EnableRaisingEvents = true; _fsw.IncludeSubdirectories = includeSubdirectories; } /// <summary> /// 當檔案夾內監控內容發生變化時 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void fsw_Changed(object sender, FileSystemEventArgs e) { Thread.Sleep(3000); IEnumerable<Watcher> watchers = this._watchers.Where(wt => wt.RightPath(e.FullPath)); if (watchers != null && watchers.Count() > 0) { foreach (Watcher watcher in watchers) { watcher.Notify(); } } } /// <summary> /// 判斷檔案是否屬於當前監控的檔案夾 /// </summary> /// <param name="fullFilePath">要判斷的檔案完整路徑</param> /// <returns></returns> internal bool FilesIn(string fullFilePath) { FileInfo fi = new FileInfo(fullFilePath); bool isIn = false; string watchPath = new DirectoryInfo(_fsw.Path).FullName; DirectoryInfo di = fi.Directory; do { if (di != null) { isIn = di.FullName == watchPath; if (!isIn) { di = di.Parent; } } else { break; } if (!this._fsw.IncludeSubdirectories) { break; } } while (!isIn); return isIn; } /// <summary> /// 添加被通知者 /// </summary> /// <param name="afw"></param> public void Attach(Watcher wt) { if (!this._watchers.Contains(wt)) { this._watchers.Add(wt); } } /// <summary> /// 移除被通知者 /// </summary> /// <param name="afw"></param> public void Detch(Watcher wt) { this._watchers.Remove(wt); } }
Watcher.cs,通過事件實現配置變更時同步通知,因為FileSystemWatcher的Changed 事件觸發時會激發兩次,所以利用lastWriteTime來阻止重複變更通知事件,順便練習了下自訂異常和自訂EventArgs
public delegate void ChangedEventHandler(object sender, FileChangedEventArgs e); public class Watcher { public event ChangedEventHandler Changed; /// <summary> /// 觀察者 /// </summary> private Observer _observer; /// <summary> /// 檔案實體路徑 /// </summary> private readonly string _fullPath; /// <summary> /// Watcher記錄到的檔案最後修改時間 /// </summary> private DateTime _lastWriteTime; public Observer Observer { get { return this._observer; } } public Watcher(string fullPath, Observer observer) { this._fullPath = fullPath.ToStandardPath(); this._lastWriteTime = File.GetLastWriteTime(this._fullPath); if (!observer.FilesIn(this._fullPath)) { throw new FileNotInDirectoryException("指定檔案並不存在於指定檔案夾內", this._fullPath, observer.FullPath); } this._observer = observer; this.Attach(); } public void Attach() { this._observer.Attach(this); } public void Detch() { this._observer.Detch(this); } internal void Notify() { DateTime filesLastWriteTime = File.GetLastWriteTime(this._fullPath); if (this._lastWriteTime != filesLastWriteTime) { this._lastWriteTime = filesLastWriteTime; this.OnChange(new FileChangedEventArgs(this._fullPath)); } } protected virtual void OnChange(FileChangedEventArgs e) { if (Changed != null) { Changed(this, e); } } internal bool RightPath(string fullPath) { return this._fullPath == fullPath; } } /// <summary> /// 包含變化檔案相關的資料 /// </summary> [Serializable] [System.Runtime.InteropServices.ComVisible(true)] public class FileChangedEventArgs : EventArgs { private readonly string _fullPath; public FileChangedEventArgs(string fullPath) { this._fullPath = fullPath.ToStandardPath(); } public string FullPath { get { return this._fullPath; } } } /// <summary> /// 當指定檔案不存在於指定檔案夾內時引發異常 /// </summary> [Serializable] [System.Runtime.InteropServices.ComVisible(true)] public class FileNotInDirectoryException : IOException { private readonly string _fileName, _directoryName; public FileNotInDirectoryException() : base("File Not In Directory") { } public FileNotInDirectoryException(string message) : base(message) { } public FileNotInDirectoryException(string message, Exception innerException) : base(message, innerException) { } // // 摘要: // 使用指定錯誤資訊和對作為此異常原因的內部異常的引用來初始化 System.IO.FileNotFoundException 類的新執行個體。 // // 參數: // message: // 解釋異常原因的錯誤資訊。 // // fileName: // 一個 System.String,它包含要驗證的檔案的完整路徑 // // directoryName // 一個 System.String,它包含要驗證的檔案夾的完整路徑 // // innerException: // 導致當前異常的異常。如果 innerException 參數不為 null,則當前異常在處理內部異常的 catch 塊中引發。 public FileNotInDirectoryException(string message, string fileName, string directoryName) : base(message) { this._fileName = fileName; this._directoryName = directoryName; } // // 摘要: // 用指定的序列化和上下文資訊初始化 System.IO.IOException 類的新執行個體。 // // 參數: // info: // 用於序列化或還原序列化對象的資料。 // // context: // 對象的源和目標。 protected FileNotInDirectoryException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info, context) { } public FileNotInDirectoryException(string message, string fileName, string directoryName, Exception innerException) : base(message, innerException) { this._fileName = fileName; this._directoryName = directoryName; } public string FileName { get { return this._fileName; } } public string DirectoryName { get { return this._directoryName; } } public override string ToString() { return "File:" + this._fileName + Environment.NewLine + "Directory:" + this._directoryName + Environment.NewLine + base.ToString(); } } internal static class FullPathHelper { public static string ToStandardPath(this string fullPath) { return fullPath.Replace('/', '\\'); } }
使用代碼
public class Demo { private Watcher _watcher; private Observer _observer; public Demo() { this._observer = new Observer(); string fullPath = AppDomain.CurrentDomain.SetupInformation.ApplicationBase + "Config\\SSOConfig.xml"; this._watcher = new Watcher(fullPath, this._observer); this._watcher.Changed += new ChangedEventHandler(_watcher_Changed); this.InitParameters(fullPath); } private void _watcher_Changed(object sender, FileChangedEventArgs e) { this.InitParameters(e.FullPath); } private void InitParameters(string fullPath) { }}
有些代碼還是寫的不好,有點重複的感覺,沒有定義介面或抽象,因為我只針對檔案變更監控,沒有預見可能會變更的地方,如果要實現監控其他的變更的話,可以修改相應的監控代碼,只要是FileSystemWatcher能監控的