Monitorowanie katalogu pod kątem tworzenia nowych plików bez FileSystemWatcher

Muszę utworzyć usługę Windows, która monitoruje określony folder pod kątem nowych plików i przetwarza go i przenosi do innej lokalizacji.

Zacząłem od używania FileSystemWatcher. Mój szef nie lubi FileSystemWatcher i chce, żebym korzystał z ankiet za pomocą Timer lub innego mechanizmu innego niż FileSystemWatcher.

Jak można monitorować Katalogowanie bez używania FileSystemWatcher za pomocą. NET framework?

Author: p.campbell, 2010-08-31

8 answers

Korzystając z odpowiedzi @Petoj dodałem pełną usługę windows, która co pięć minut przeprowadza ankiety na nowe pliki. Jego kontrastuje więc tylko jeden wątek ankiety, konta dla czasu przetwarzania i obsługuje pauzy i terminowe zatrzymanie. Obsługuje również łatwe dołączanie debbuggera w systemie.start

 public partial class Service : ServiceBase{


    List<string> fileList = new List<string>();

    System.Timers.Timer timer;


    public Service()
    {
        timer = new System.Timers.Timer();
        //When autoreset is True there are reentrancy problems.
        timer.AutoReset = false;

        timer.Elapsed += new System.Timers.ElapsedEventHandler(DoStuff);
    }


    private void DoStuff(object sender, System.Timers.ElapsedEventArgs e)
    {
       LastChecked = DateTime.Now;

       string[] files = System.IO.Directory.GetFiles("c:\\", "*", System.IO.SearchOption.AllDirectories);

       foreach (string file in files)
       {
           if (!fileList.Contains(file))
           {
               fileList.Add(file);

               do_some_processing();
           }
       }


       TimeSpan ts = DateTime.Now.Subtract(LastChecked);
       TimeSpan MaxWaitTime = TimeSpan.FromMinutes(5);

       if (MaxWaitTime.Subtract(ts).CompareTo(TimeSpan.Zero) > -1)
           timer.Interval = MaxWaitTime.Subtract(ts).TotalMilliseconds;
       else
           timer.Interval = 1;

       timer.Start();
    }

    protected override void OnPause()
    {
        base.OnPause();
        this.timer.Stop();
    }

    protected override void OnContinue()
    {
        base.OnContinue();
        this.timer.Interval = 1;
        this.timer.Start();
    }

    protected override void OnStop()
    {
        base.OnStop();
        this.timer.Stop();
    }

    protected override void OnStart(string[] args)
    {
       foreach (string arg in args)
       {
           if (arg == "DEBUG_SERVICE")
                   DebugMode();

       }

        #if DEBUG
            DebugMode();
        #endif

        timer.Interval = 1;
        timer.Start();
   }

   private static void DebugMode()
   {
       Debugger.Break();
   }

 }
 6
Author: Conrad Frix,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-27 16:23:02

Właściwie, komponent FileWatcher nie jest w 100% "stabilny" z mojego doświadczenia na przestrzeni lat. Wciśnij wystarczającą ilość plików do folderu, a stracisz niektóre zdarzenia. Jest to szczególnie ważne, jeśli monitorujesz udział plików, nawet jeśli zwiększysz rozmiar bufora.

Więc, ze wszystkich praktycznych powodów, użyj FileWatcher wraz z timerem, który skanuje folder w poszukiwaniu zmian, aby znaleźć najbardziej optymalne rozwiązanie.

Przykłady tworzenia kodu timera powinny być obfite, jeśli go wygooglujesz. If you keep śledź ostatnią DateTime, kiedy uruchomił się timer, a następnie sprawdź zmodyfikowaną datę KAŻDEGO pliku i porównaj ją z datą. Dość prosta logika.

Interwał czasowy zależy od tego, jak pilne są zmiany w Twoim systemie. Ale sprawdź co minutę powinno być dobrze dla wielu scenariuszy.

 17
Author: Mikael Svenson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-09-30 17:07:10

Podczas uruchamiania programu użyj katalogu.GetFiles (ścieżka) do pobrania listy plików.

Następnie utwórz timer, a w jego upłynął wywołanie zdarzenia manewfiles:

    static List<string> hasNewFiles(string path, List<string> lastKnownFiles)
    {
        List<string> files = Directory.GetFiles(path).ToList();
        List<string> newFiles = new List<string>();

        foreach (string s in files)
        {
            if (!lastKnownFiles.Contains(s))
                newFiles.Add(s);
        }

        return new List<string>();
    }

W kodzie wywołującym, będziesz miał nowe pliki, Jeśli:

    List<string> newFiles = hasNewFiles(path, lastKnownFiles);
    if (newFiles.Count > 0)
    {
        processFiles(newFiles);
        lastKnownFiles = newFiles;
    }

Edit: jeśli chcesz bardziej linqy rozwiązanie:

    static IEnumerable<string> hasNewFiles(string path, List<string> lastKnownFiles)
    {
        return from f in Directory.GetFiles(path) 
               where !lastKnownFiles.Contains(f) 
               select f;
    }

    List<string> newFiles = hasNewFiles(path, lastKnownFiles); 
    if (newFiles.Count() > 0) 
    { 
        processFiles(newFiles); 
        lastKnownFiles = newFiles; 
    } 
 4
Author: Steven Evers,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-31 19:35:45

Możesz użyć Directory.GetFiles():

using System.IO;

var fileList = new List<string>();

foreach (var file in Directory.GetFiles(@"c:\", "*", SearchOption.AllDirectories))
{
    if (!fileList.Contains(file))
    {
        fileList.Add(file);
        //do something
    }
}

Uwaga ta sprawdza tylko nowe pliki nie zmienione pliki, jeśli potrzebujesz, aby użyć FileInfo

 4
Author: Peter,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-10-10 20:07:14

Chciałbym zapytać, dlaczego nie używać FileSystemWatcher. Rejestruje się w systemie operacyjnym i jest powiadamiany natychmiast po zakończeniu zdarzenia w systemie plików.

Jeśli naprawdę musisz sondować, to po prostu stwórz System.Zegary.Timer, Utwórz metodę do wywołania i sprawdź plik w tej metodzie.

 1
Author: davisoa,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-31 19:10:59

Tak, możesz utworzyć Timer i podłączyć obsługę do zdarzenia, które utworzy instancję klasy DirectoryInfo dla obserwowanego katalogu i wywoła GetFiles () lub EnumerateFiles (). GetFiles () zwraca tablicę FileInfo [], podczas gdy EnumerateFiles () Zwraca liczbę" strumieniową". EnumerateFiles() będzie bardziej wydajne, jeśli spodziewasz się, że wiele plików będzie w tym folderze podczas wyszukiwania; możesz rozpocząć pracę z IEnumerable, zanim metoda pobierze wszystkie FileInfos, natomiast GetFiles sprawi, że będziesz czekał.

Co do tego, dlaczego może to być lepsze niż FileWatcher, to zależy od architektury za kulisami. Weźmy na przykład podstawowy przepływ pracy Wyodrębnij/Przekształć/Zwaliduj / załaduj. Po pierwsze, taki przepływ pracy może wymagać tworzenia kosztownych instancji obiektów (połączeń DB, instancji mechanizmu reguł itp.). Ten jednorazowy narzut jest znacznie zmniejszony, jeśli przepływ pracy jest zorganizowany tak, aby obsługiwać wszystko, co jest dostępne za jednym zamachem. Drugi, FileWatcher wymagałby, aby wszystko, co wywołane przez procedury obsługi zdarzeń, takie jak ten przepływ pracy, było bezpieczne dla wątków, ponieważ wiele zdarzeń może być uruchomionych jednocześnie, jeśli pliki są stale napływające. Jeśli nie jest to wykonalne, Timer może być bardzo łatwo skonfigurowany tak, aby ograniczyć system do jednego uruchomionego przepływu pracy, przez to, że procedury obsługi zdarzeń sprawdzają bezpieczną dla wątku flagę "process running" i po prostu kończą, jeśli inny wątek obsługi ustawił ją i nie został jeszcze zakończony. Pliki w folderze w tym czasie zostaną odebrane następnym razem, gdy Timer zostanie wywołany, w przeciwieństwie do FileWatcher, gdzie jeśli zakończysz obsługę, informacja o istnieniu tego pliku zostanie utracona.

 1
Author: KeithS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-31 21:32:54

1) brzmi jak twój szef jest idiotą
2) będziesz musiał użyć funkcji takich jak katalog.GetFiles, File.GetLastAccessTime, itp. i zachować go w pamięci, aby sprawdzić, czy się zmienił.

 0
Author: Wildhorn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-31 19:20:32

To trochę dziwne, że nie można używać FileSystemWatcher lub prawdopodobnie żadnego z API Win32, które robią to samo, ale to jest nieistotne w tym momencie. Metoda sondażowa może wyglądać tak.

public class WorseFileSystemWatcher : IDisposable
{
  private ManaulResetEvent m_Stop = new ManaulResetEvent(false);

  public event EventHandler Change;

  public WorseFileSystemWatcher(TimeSpan pollingInterval)
  {
    var thread = new Thread(
      () =>
      {
        while (!m_Stop.WaitOne(pollingInterval))
        {
          // Add your code to check for changes here.
          if (/* change detected */)
          {
            if (Change != null)
            {
              Change(this, new EventArgs())
            }
          }
        }
      });
    thread.Start();
  }

  public void Dispose()
  {
    m_Stop.Set();
  }
}
 0
Author: Brian Gideon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-08-31 19:48:47