FileSystemWatcher: игнорировать первое изменение в файле - PullRequest
1 голос
/ 24 апреля 2019

Я использую FileSystemWatcher класс в C# для отслеживания изменений в файле всякий раз, когда пользователь сохраняет файл после его изменения.

FileSystemWatcher watcher = new FileSystemWatcher()
{
    Path = DIR_NAME,
    NotifyFilter = NotifyFilters.LastWrite | 
                   NotifyFilters.CreationTime | 
                   NotifyFilters.LastAccess,
    Filter = "*.pdf",
    IncludeSubdirectories = true,
    EnableRaisingEvents = true
};

watcher.Changed += OnChanged;

Однако файл, который я хочу отслеживать, создается программно, как показано ниже:

FileStream stream = FileUtil.CreateNewFile(filePath); // Creates a file
file.WriteFileToStream(stream); // Writes into the file

В идеале мой код должен работать следующим образом:

  1. Программа создаст файл и запишет в него некоторый контент.
  2. Пользователь открывает файл, изменяет его и сохраняет.
  3. В этот момент он должен вызвать OnChanged, т.е. я хочу, чтобы мой код обработчика OnChanged выполнялся только тогда, когда реальный пользователь изменяет его и сохраняет его.

Однако он запускается всякий раз, когда файл записывается программным способом, т. Е. В следующей строке:

file.WriteFileToStream(stream);

Технически правильное поведение, так как он отслеживаетизменить в файле.Однако мой бизнес-пример не позволяет выполнять код обработчика OnChanged при первоначальном создании и записи файла.

У меня такой вопрос, есть ли способ пропустить вызов OnChanged, когда файл создается и записывается в первый раз программно?

Примечание:

  1. Архитектура приложения требует, чтобы FileSystemWatcher инициализировался при запуске моего приложения.Поэтому я не могу зарегистрировать его после создания файла.
  2. Это многопользовательское приложение, в которое одновременно будут записывать файлы несколько пользователей, поэтому я не могу отключить наблюдатель перед созданием файла и включить его после его создания:

watcher.EnableRaisingEvents = false; 
CreateFile();
watcher.EnableRaisingEvents = true;

Ответы [ 4 ]

1 голос
/ 24 апреля 2019

Первый подход:

  1. Создайте и сохраните файл в каталоге, который не просматривается.
  2. Переместить файл в каталог для просмотра.

Второй подход:

Когда файл создан, используйте обработчик событий OnCreated(), чтобы добавить имя файла в список filesToIgnore.

В обработчике событий OnChanged проверьте, содержит ли список filesToIgnore имя файла. Если это так, удалите его из списка (чтобы обработать его в следующий раз) и вернитесь из обработчика.

private List<string> filesToIgnore = new List<string>();

private void OnCreated(object source, FileSystemEventArgs file)
{
   filesToIgnore.Add(file.Name);
}

private void OnChanged(object source, FileSystemEventArgs file)
{
    if(filesToIgnore.Contains(file.Name))
    {
         filesToIgnore.Remove(file.Name);
         return; 
    }

    // Code to execute when user saves the file
}

При таком подходе предполагается, что OnCreated() будет всегда запускаться до OnChanged().

0 голосов
/ 24 апреля 2019

Метод расширения ниже позволяет обрабатывать события только от действий пользователя:

public static void OnChangedByUser(this FileSystemWatcher fsw,
    FileSystemEventHandler handler)
{
    const int TOLERANCE_MSEC = 100;
    object locker = new object();
    string fileName = null;
    Stopwatch stopwatch = new Stopwatch();
    fsw.Created += OnFileCreated;
    fsw.Changed += OnFileChanged;
    fsw.Disposed += OnDisposed;
    void OnFileCreated(object sender, FileSystemEventArgs e)
    {
        lock (locker)
        {
            fileName = e.Name;
            stopwatch.Restart();
        }
    }
    void OnFileChanged(object sender, FileSystemEventArgs e)
    {
        lock (locker)
        {
            if (e.Name == fileName && stopwatch.ElapsedMilliseconds < TOLERANCE_MSEC)
            {
                return; // Ignore this event
            }
        }
        handler.Invoke(sender, e);
    }
    void OnDisposed(object sender, EventArgs e)
    {
        fsw.Created -= OnFileCreated;
        fsw.Changed -= OnFileChanged;
        fsw.Disposed -= OnDisposed;
    }
}

Пример использования:

var fsw = new FileSystemWatcher(DIR_NAME);
fsw.OnChangedByUser(File_ChangedByUser);
fsw.EnableRaisingEvents = true;

private static void File_ChangedByUser(object sender, FileSystemEventArgs e)
{
    // Handle the event
}
0 голосов
/ 24 апреля 2019

Я добавил следующий код в обработчик OnChanged, и он, кажется, работает должным образом.

private void OnChanged(object source, FileSystemEventArgs file)
{
    if (file.ChangeType == WatcherChangeTypes.Created)
        return;

    FileInfo fileInfo = new FileInfo(file.FullPath);
    if (fileInfo.CreationTime == fileInfo.LastWriteTime)
        return; 

    // Handle the Changed event
    ...
}

Однако я не уверен, что мне не хватает чего-то, что может привести к поломке. Есть мысли?

0 голосов
/ 24 апреля 2019

Это неприятная ситуация, и вы ничего не можете с этим поделать, кроме проверки, заблокирован ли файл (при условии, что он заблокирован для чтения или записи)

public  bool IsFileLocked(string fileName)
{

   try
   {
      using (var stream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
      {
         return false;
      }   
   }
   catch (IOException)
   {
      //the file is unavailable
      return true;
   }
}

Предполагается, что когда вы получаете обновление, вы проверяете, заблокирован ли файл, если нет, то вы можете свободно предположить, что оно закрыто.

Некоторые вещи, которые следует учитывать, хотя

  1. Может быть состояние гонки, когда проверка файла заблокирована, получение полной очистки и затем обнаружение какого-либо другого процесса заблокировало файл.
  2. Я использовал FileAccess.Read, так что это не сбой для файлов только для чтения.
  3. События могут быть отброшены из FileSystemWatcher по разным причинам, и на них нельзя всегда полагаться в определенных ситуациях. Прочтите документацию о причинах, по которым события могут быть отброшены, и о том, как их исправить. В этих случаях вы могли бы избежать длинного опроса и некоторой логики, чтобы подобрать всех отставших (в зависимости от вашей ситуации)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...