代码改变世界

使用FileSystemWatcher监视文件更改

2010-09-20 18:54 囧月 阅读(...) 评论(...) 编辑 收藏

    FileSystemWatcher相关介绍请看MSDN(http://msdn.microsoft.com/zh-cn/library/system.io.filesystemwatcher.aspx),本文只是对Changed事件多次触发做一点处理(纯粹个人见解)。

    MSDN对Changed事件的定义为:当对所监视的目录中的文件或目录的大小、系统特性、上次写入时间、上次访问时间或安全权限进行更改时,将引发 Changed 事件。

 

    首先定义一个基础类型:

    public class FileWatcher
    {
        FileSystemWatcher fsw;
        public event Action<FileSystemEventArgs> OnChange;

        public FileWatcher(string path, string filter)
        {
            fsw = new FileSystemWatcher();
            fsw.Path = path;
            fsw.Filter = filter;
            fsw.NotifyFilter = NotifyFilters.LastWrite;
            fsw.Changed += fsw_Changed;
            fsw.EnableRaisingEvents = true;
        }

        void fsw_Changed(object sender, FileSystemEventArgs e)
        {
            if (CanRaiseChange(e))
                RaiseChange(e);
        }

        protected virtual bool CanRaiseChange(FileSystemEventArgs e)
        {
            return true;
        }

        protected virtual void RaiseChange(FileSystemEventArgs e)
        {
            if (OnChange != null)
                OnChange.BeginInvoke(e, null, null);//考虑到OnChange执行时间有可能比较长
        }
    }

 

    因为只需要监视文件内容更改,仅仅监视文件最后写入时间已经足够。在本文之前,找了很多网站,均是以System.Threading.Timer来解决多次触发问题,但由于无法对Timer.Change方法传递额外的参数,在准确性上未免打一些折扣,而且某些解决方案无法处理同一时间修改的多个文件,遂决定自己实现。

    具体思路为:
    1、首先创建一个字典,以文件的完整路径为Key,最后触发Changed时间为Value
    2、对于每个触发的Changed事件,首先判断被修改文件的完整路径是否在字典中,如果不存在则向字典添加
    3、如果存在于字典中,则判断当前被修改文件上一次Changed事件触发的时间与当前触发时间间隔,这里暂且设置为500毫秒
    4、对于500毫秒以内的不作处理
    5、对于超出500毫秒的进行处理,并更新当前被修改文件的Changed事件触发时间

 

    代码如下:

    public class MyFileWatcher : FileWatcher
    {
        private Dictionary<string, DateTime> changesHistory = new Dictionary<string, DateTime>();

        public MyFileWatcher(string path, string filter) :
            base(path, filter) { }

        protected override bool CanRaiseChange(FileSystemEventArgs e)
        {
            if (!changesHistory.ContainsKey(e.FullPath))
            {
                Console.WriteLine("a");
                changesHistory.Add(e.FullPath, DateTime.Now);
                return true;
            }
            
            DateTime now = DateTime.Now;
            bool canRaise = (now - changesHistory[e.FullPath]).TotalMilliseconds >= 500;
            if (canRaise)
                changesHistory[e.FullPath] = now;
            Console.WriteLine(canRaise);
            return canRaise;
        }
    }

 

    测试代码:

    public static void Main(string[] args)
    {
        MyFileWatcher mfw = new MyFileWatcher("g:\\test\\", "*.txt");
        mfw.OnChange += new Action<FileSystemEventArgs>(fw_OnChange);
        Console.Read();
    }
    
    static void fw_OnChange(FileSystemEventArgs obj)
    {
        Console.WriteLine("{0} {1}", DateTime.Now, obj.FullPath);
    }

 

    本方法存在一个问题,就是需要监视的文件增多以后,changesHistory字典会不断增大,对性能会有一定影响。可以考虑在CanRaiseChange方法里删除一部分时间间隔比较长的元素。

    另外,对于多线程同时修改一个文件的情况,本方法也无法解决,但已经能满足我的需求。