Содержание
При разработке программ, активно использующих информацию о файловой системе, часто возникает необходимость получать и отслеживать изменения, происходящие с файлами и каталогами. Наиболее ярким примером такого слежения являются всякого рода клиенты для облачных хранилищ типа Яндекс.Диск, Dropbox и т.д. Такие клиенты следят за изменениями в конкретной папке на диске и при малейшем изменении инициируют процесс синхронизации с облаком. Сегодня мы рассмотрим то, как организовать отслеживание изменений в файловой системе с использованием механизмов предоставляемых в .NET C#
Класс File System Watcher
Класс File
расположен в пространстве имен System.IO
. Этот класс ожидает уведомления файловой системы об изменениях и инициирует события при изменениях каталога или файла в каталоге. Ниже представлены основные свойства, методы и события класса File
- Свойство
Enable
— получает или задает значение, определяющее, доступен ли данный компонентRaising Events - Свойство
Events
— возвращает список обработчиков событий, которые прикреплены к этому объекту - Свойство
Filter
— получает или задает строку фильтра, используемую для определения файлов, контролируемых в каталоге - Свойство
Filters
— возвращает коллекцию всех фильтров, используемых для определения файлов, контролируемых в каталоге - Свойство
Include
— получает или задает значение, показывающее необходимость контроля вложенных каталогов по указанному пути.Subdirectories - Свойство
Internal
— получает или задает размер (в байтах) внутреннего буфера.Buffer Size - Свойство
Notify
— получает или задает тип отслеживаемых измененийFilter - Свойство
Path
— возвращает или задает путь отслеживаемого каталога - Метод
Wait
— синхронный метод, возвращающий структуру, содержащую сведения о произошедших изменениях, с заданным типом изменений, которые вы хотите контролироватьFor Changed(Watcher Change Types) - Метод
Wait
— синхронный метод, возвращающий структуру, содержащую сведения о произошедших изменениях, с заданным типом изменений, которые вы хотите контролировать, и временем ожидания (в мс) до блокировки по ожиданиюFor Changed(Watcher Change Types, Int32) - Событие
Changed
— происходит при изменении файла или каталога по заданному путиPath
. - Событие
Created
— происходит при создании файла или каталога по заданному путиPath
. - Событие
Deleted
— происходит при удалении файла или каталога по заданному путиPath
. - Событие
Error
— происходит, когда экземпляруFileSystemWatcher
не удается продолжить отслеживание изменений, например, при переполнении внутреннего буфера. - Событие
Renamed
— происходит при переименовании файла или каталога по заданному путиPath
.
Для работы с классом File
мы должны создать объект этого класса, назначить для объекта тип отслеживаемых изменений, каталог, в котором эти изменения будут отслеживаться и обработчики событий. Рассмотрим несколько примеров использования класса File
Как в C# отследить изменение имени файла?
Для отслеживания изменений только в имени файла нам достаточно назначить свойству Notify
значение NotifyFilters.FileName
, написать обработчик и назначить его событию Renamed
using System; using System.IO; namespace FileSystem { class Program { static void Main(string[] args) { string path = @"c:\CSharp Output\"; string fileName = "File.txt"; FileSystemWatcher watcher = new FileSystemWatcher(path, fileName); watcher.NotifyFilter = NotifyFilters.FileName; watcher.Renamed += RenamedEventHandler; watcher.EnableRaisingEvents = true; Console.WriteLine("Press enter to exit."); Console.ReadLine(); } private static void RenamedEventHandler(object sender, RenamedEventArgs e) { Console.WriteLine($"Старое имя файла: {e.OldName}"); Console.WriteLine($"Новое имя файла: {e.Name}"); ((FileSystemWatcher)sender).Filter = e.Name; Console.WriteLine($"Теперь отслеживаем файл: {e.Name}"); } } }
Приложение отслеживает изменение имени файла, расположенного по пути c:\CSharp Output\File.txt
При этом, при очередном изменении имени файла у объекта класса FileSystemWatcher
переопределяется свойство Filter
для того, чтобы изменения в имени файла продолжали отслеживаться. Результат работы программы может быть примерно следующим:
Старое имя файла: File.txt
Новое имя файла: МойФайл.txt
Теперь отслеживаем файл: МойФайл.txt
Старое имя файла: МойФайл.txt
Новое имя файла: MyFile.txt
Теперь отслеживаем файл: MyFile.txt
Старое имя файла: MyFile.txt
Новое имя файла: MyFile.pdf
Теперь отслеживаем файл: MyFile.pdf
Как в C# отслеживать изменения всех файлов и каталогов в определенном каталоге и всех его подкаталогах?
Отслеживание всех изменений в файловой системе немногим сложнее, чем отслеживание конкретного файла, как было показано в предыдущем примере. Необходимо также назначить определенные фильтры и написать обработчики для событий компонента. Например, вот так:
using System; using System.IO; namespace FileSystem { class Program { static void Main() { using var watcher = new FileSystemWatcher(@"c:\CSharp Output\"); watcher.NotifyFilter = NotifyFilters.Attributes | NotifyFilters.CreationTime | NotifyFilters.DirectoryName | NotifyFilters.FileName | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.Security | NotifyFilters.Size; watcher.Changed += OnChanged; watcher.Created += OnCreated; watcher.Deleted += OnDeleted; watcher.Renamed += OnRenamed; watcher.Error += OnError; watcher.Filter = "*.*"; watcher.IncludeSubdirectories = true; watcher.EnableRaisingEvents = true; Console.WriteLine("Press enter to exit."); Console.ReadLine(); } private static void OnChanged(object sender, FileSystemEventArgs e) { if (e.ChangeType != WatcherChangeTypes.Changed) { return; } Console.WriteLine($"Изменение элемента: {e.FullPath}"); } private static void OnCreated(object sender, FileSystemEventArgs e) { string value = $"Создан элемент: {e.FullPath}"; Console.WriteLine(value); } private static void OnDeleted(object sender, FileSystemEventArgs e) => Console.WriteLine($"Удален элемент: {e.FullPath}"); private static void OnRenamed(object sender, RenamedEventArgs e) { Console.WriteLine($"Элемент переименован:"); Console.WriteLine($" Старое имя: {e.OldFullPath}"); Console.WriteLine($" Новое имя: {e.FullPath}"); } private static void OnError(object sender, ErrorEventArgs e) => PrintException(e.GetException()); private static void PrintException(Exception? ex) { if (ex != null) { Console.WriteLine($"Сообщение об ошибке: {ex.Message}"); Console.WriteLine("Трассировка стека:"); Console.WriteLine(ex.StackTrace); Console.WriteLine(); PrintException(ex.InnerException); } } } }
Здесь стоит обратить внимание на следующие строки:
watcher.Filter = "*.*"; watcher.IncludeSubdirectories = true;
Таким образом мы указали, что отслеживаться будет абсолютно все элементы, включая вложенные папки. Результат работы программы может быть следующим:
Элемент переименован:
Старое имя: c:\CSharp Output\MyFile.pdf
Новое имя: c:\CSharp Output\MyFile.txt
Удален элемент: c:\CSharp Output\MyFile.txt
Элемент переименован:
Старое имя: c:\CSharp Output\SubDir
Новое имя: c:\CSharp Output\Новая папка
Изменение элемента: c:\CSharp Output\Новая папка
Удален элемент: c:\CSharp Output\Новая папка
На что обратить внимание при работе с классом FileSystemWatcher
Все изменения, произошедшие в файловой системе хранятся во внутреннем буфере FileSystemWatcher
максимальный размер которого может составлять 64 Кб. Таким образом, если за короткий промежуток времени произойдет большое количество изменений в отслеживаемом каталоге, то это может привести к переполнению буфера и, соответственно, вызову исключения. Чтобы попытаться избежать подобного исключения можно:
- Установить только те фильтры в свойстве
NotifyFilter
, которые вам действительно необходимы для работы - Установить значение
Internal
максимальным, если предполагается, что в каталоге может произойти большое количество изменений в течение короткого промежутка времениBuffer Size - Делать обработчики событий у
FileSystemWatcher
настолько короткими, насколько это возможно, чтобы не «забивать» буфер необработанными изменениями.
Итого
Сегодня мы рассмотрели основные возможности C# для отслеживания изменений в каталогах с использованием класса FileSystemWatcher
. Этот класс позволяет отслеживать различные типы изменений файловой системы, включая как изменения, произошедшие в конкретном файле, так и изменения произошедшие во всех подкаталогах отслеживаемого каталога.