Tuesday, 27 July 2010

FileSystemWatcher для джуніора

Intro
Сьогодні на роботі переді мною повстало завдання, написати код який мав би слідкував за певною директорією і при додаванні, чи видаленні файлів з неї, записував список файлів в xml документ.
Першою думкою було написати евент, який при кожній активності користувача в програмі обновляв би список файлів і створював відповідну хмлку. Я навіть не встиг потішитись з такого швидкого вирішення проблеми як мене перебили і сказали що для цього можу використати FileSystemWatcher та дали ссилку на мсдн. В цей момент ейфорія розвіялась я опустився на землю і ще раз переконався в тому що я JUNIOR.

Code

using System;
using System.IO;
using System.Security.Permissions;

namespace Wizard.BizLogic
{
  public class FolderWatcher
  {
    /// <summary>
    /// Sets the folder to watch
    /// </summary>
    /// <param name="path">Patch to the folder</param>
    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    public static void WatchFolder(string path)
    {
      FileSystemWatcher watcher = new FileSystemWatcher();
      watcher.Path = path;
      watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
        | NotifyFilters.FileName | NotifyFilters.DirectoryName;

      watcher.Filter = "*.xml";
      watcher.IncludeSubdirectories = true;

      watcher.Created += new FileSystemEventHandler(onChanged);
      watcher.Deleted += new FileSystemEventHandler(onChanged);
      watcher.Renamed += new RenamedEventHandler(onRenamed);

      watcher.EnableRaisingEvents = true;
    }

    private static void onChanged(object source, FileSystemEventArgs e)
    {
      string fullPath = e.FullPath;
      DirectoryInfo directoryInfo = Directory.GetParent(fullPath);
      string[] strings = e.Name.Split("\\");
      if (strings[1] != directoryInfo.Name + ".xml")
      {
        string xmlFileList = MigrationSchemaLogic.GetXMLFileList(directoryInfo.FullName);
      }
    }

    private static void onRenamed(object source, RenamedEventArgs e)
    {
      string fullPath = e.FullPath;
      DirectoryInfo directoryInfo = Directory.GetParent(fullPath);
      string[] strings = e.Name.Split("
\\");
      if (strings[1] != directoryInfo.Name + ".xml")
      {
        string xmlFileList = MigrationSchemaLogic.GetXMLFileList(directoryInfo.FullName);
      }
    }
  }
}


* This source code was highlighted with Source Code Highlighter.

Тут все просто, в класі є три методи, всі вони статичні, але лише до одного з них є доступ з зовні, це WatchFolder(string path) який на вхід приймає стрічку з адресою папки типу "C:\temp". Далі створюється об'єкт watcher, для якого ми задаємо фільтри при яких він буде спрацьовувати (тут детальніше) і при винекненні певних змін в каталозі будуть відповідно викликатись методи
watcher.Created += new FileSystemEventHandler(onChanged);
watcher.Deleted += new FileSystemEventHandler(onChanged);
watcher.Renamed += new RenamedEventHandler(onRenamed);
В мене в методах зроблена перевірка на те якщо зміни відбулись в моїй хмлці то ми їх ігноруємо, якщо ні то викликаємо метод для перерахунку файлів у каталозі.
Код максимально простий і думаю для вас не буде важко його використати. Викликається він просто "FolderWatcher.WatchFolder(c:\Windows)" а для отримання практичного результату необхідно створити свій метод і визвати його замість
"string xmlFileList = MigrationSchemaLogic.GetXMLFileList(directoryInfo.FullName);".

GetXMLFileList Listing:
public static string GetXMLFileList(string directory)
    {
      StringWriter writerString = new StringWriter();
      XmlTextWriter writer = new XmlTextWriter(writerString);
      writer.WriteStartDocument(true);
      bool bFirst = true;
      DirectoryInfo dir = new DirectoryInfo(directory);
      if (dir.GetFiles("*.xml").Count() > 0)
      {
        foreach (FileInfo entry in dir.GetFiles("*.xml"))
        {
          if (bFirst)
          {
            writer.WriteStartElement("XMLFilesList", string.Empty);

            string strFullName = entry.FullName;
            string strFileName = entry.Name;
            string strDir = strFullName.Substring(0, strFullName.Length - strFileName.Length);

            writer.WriteAttributeString("Directory", strDir);
            bFirst = false;
          }
          if (entry.Name != dir.Name + ".xml")
          {
            writer.WriteStartElement("file", string.Empty);

            writer.WriteElementString("FileName", entry.Name);
            writer.WriteElementString("Path", String.Concat(@"..\..\..\Xml\", entry.Name));

            writer.WriteEndElement();
          }
        }

        writer.WriteEndElement();
        writer.WriteEndDocument();
      }
      //FileStream fs = File.Open(directory + "
\\" + dir.Name + ".xml", FileMode.Create, FileAccess.Write);
      FileStream fs = File.Open(String.Concat(directory, "
\\", dir.Name, ".xml"), FileMode.Create, FileAccess.Write);
      StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);

      sw.WriteLine(writerString.ToString());

      sw.Flush();
      sw.Close();
      return writerString.ToString();
    }

* This source code was highlighted with Source Code Highlighter.

PS
Хоча я й був розчарований своїм незнанням, я дуже зрадів що можу вивчити щось нове, нехай таке маленьке й незначне але все-одно це ще один сантиметр на шляху до успіху. Пам'ятайте : "Одні люди перед собою бачить проблеми, а інші можливості" кожна проблема це можливість вивчити щось нове так що не втрачайте їх.
Дякую за увагу.