Содержание
При разработке программ, активно использующих информацию о файловой системе, часто возникает необходимость получать и отслеживать изменения, происходящие с файлами и каталогами. Наиболее ярким примером такого слежения являются всякого рода клиенты для облачных хранилищ типа Яндекс.Диск, 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 . Этот класс позволяет отслеживать различные типы изменений файловой системы, включая как изменения, произошедшие в конкретном файле, так и изменения произошедшие во всех подкаталогах отслеживаемого каталога.