因为经常要写各种配置文件,所以曾经写过监控文件变更的方法,后来其他地方要用到时每次都要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能监控的 :P