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