使用FileSystemWatcher监听文件状态


更新记录
本文迁移自Panda666原博客,原发布时间:2021年7月2日。

一、FileSystemWatcher类型介绍

在.NET中使用 FileSystemWatcher 类型可以进行监视指定目录的更改。通过自定义的监听设置,可监视指定目录中的文件或子目录的更改。通过该类可以不只是监视本地计算机的文件,还可以监听网络驱动器或远程计算机的文件。

FileSystemWatcher 类型所在命名空间

using System.IO;

注意:一定要确保新建/修改等完成后再进行对文件的操作,可以检测文件是否可用,再进行操作。

二、具体实例

using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

namespace PandaTestClass
{
    class Program
    {
        /// 
        /// 打开文件,用于测试文件是否可用
        /// 
        /// 
        /// 
        /// 
        [DllImport("kernel32.dll")]
        public static extern IntPtr _lopen(string lpPathName, int iReadWrite);

        /// 
        /// 关闭句柄
        /// 
        /// 
        /// 
        [DllImport("kernel32.dll")]
        public static extern bool CloseHandle(IntPtr hObject);

        /// 
        /// 文件打开的模式,读写权限
        /// 
        public const int OF_READWRITE = 2;

        /// 
        /// 文件打开的模式,进程间共享读写
        /// 
        public const int OF_SHARE_DENY_NONE = 0x40;

        /// 
        /// 文件打开错误标志位
        /// 
        public static readonly IntPtr HFILE_ERROR = new IntPtr(-1);

        static void Main(string[] args)
        {
            //新建文件监听器
            FileSystemWatcher watcher = new FileSystemWatcher();

            //====配置文件监听器=====
            //监听的目录
            string monitoredPath = @"F:/";
            //监听的文件类型
            string monitoredFileType = "*.txt|*.cs";
            //监听的修改的具体操作类型
            NotifyFilters notifyFilters = NotifyFilters.FileName 
                                            | NotifyFilters.Size 
                                            | NotifyFilters.LastWrite;

            //设置参数
            watcher.Path = monitoredPath;
            monitoredFileType.Split("|")
                                .ToList()
                                .ForEach(elem => watcher.Filters.Add(elem));
            watcher.NotifyFilter = notifyFilters;
            //====配置文件监听器=====

            //====绑定事件处理函数====
            //(增)绑定文件创建处理函数
            watcher.Created += (object sender, FileSystemEventArgs args) => {
                Console.WriteLine("文件创建啦");
                Console.WriteLine($"文件名:{args.Name}");
                Console.WriteLine($"所在路径:{args.FullPath}");
            };

            //(删)绑定文件删除处理函数
            watcher.Deleted += (object sender, FileSystemEventArgs args) => {
                Console.WriteLine("文件删除啦");
                Console.WriteLine($"文件名{args.Name}");
                Console.WriteLine($"所在路径:{args.FullPath}");
            };

            //(改)绑定文件重命名处理函数
            watcher.Renamed += (object sender, RenamedEventArgs args) => {
                Console.WriteLine("文件重命名啦");
                Console.WriteLine($"旧文件名{args.OldName}");
                Console.WriteLine($"新文件名{args.Name}");
            };

            //(改)绑定文件修改事件处理函数
            watcher.Changed += (object sender, FileSystemEventArgs args) => {
                Console.WriteLine("文件修改啦");
                Console.WriteLine($"被修改的文件是{args.Name}");
                Console.WriteLine($"被修改的文件文件路径是{args.FullPath}");
                Console.WriteLine($"修改的类型{args.ChangeType.ToString()}");

                //====检测创建后是否可用(比如:是否复制完成)====
                //先检测文件是否存在
                FileInfo fileInfo = new(args.FullPath);
                if (!fileInfo.Exists)
                {
                    return;
                }

                //检测文件是否复制完成
                IntPtr vHandle = _lopen(args.FullPath, OF_READWRITE | OF_SHARE_DENY_NONE);
                if (vHandle == HFILE_ERROR)
                {
                    Console.WriteLine("文件还未复制完成");
                    return;
                }

                //释放句柄
                CloseHandle(vHandle);
                //可以对文件进行操作
                Console.WriteLine("可以对文件进行操作了");
                //====检测创建后是否可用(比如:是否复制完成)====

                //对文件具体操作的代码
            };

            //绑定文件错误事件处理函数
            watcher.Error += (object sender, ErrorEventArgs args) =>{
                Console.WriteLine("文件出错啦");
                Console.WriteLine($"出错信息:{args.GetException().Message}");
            };
            //====绑定事件处理函数====

            //开启监听
            watcher.EnableRaisingEvents = true;

            //等待用户关闭监听
            while (true)
            {
                Console.WriteLine("如需关闭监听,请按Y:");
                if((Console.ReadKey()).Key == ConsoleKey.Y)
                {
                    //释放监听器
                    watcher.Dispose();
                    return;
                }
            }
        }
    }
}

说明:代码中基本每条语句都有注释,也没有过多的分支,一眼就可以看明白,我也就不需要过的的解释了。但需要注意的是代码中添加了处理检测文件是否可用的代码,这些代码用到了互操作代码,如果您不需要检测文件是否可用,可以将其删除。代码为了观看方法把所有代码都写在一个方法中,如果您要用在项目中,记得遵守SOLID,将代码进行拆分成多个单独的部分。