Korzystanie z FileSystemWatcher do monitorowania katalogu

101

Używam aplikacji Windows Forms do monitorowania katalogu i przenoszenia plików upuszczonych w nim do innego katalogu.

W tej chwili skopiuje plik do innego katalogu, ale po dodaniu innego pliku zakończy się po prostu bez komunikatu o błędzie. Czasami kopiuje dwa pliki, zanim zakończy się na trzecim.

Czy to dlatego, że używam aplikacji Windows Form, a nie aplikacji konsoli? Czy jest sposób, aby zatrzymać program przed zakończeniem i dalej obserwować katalog?

private void watch()
{
  this.watcher = new FileSystemWatcher();
  watcher.Path = path;
  watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
                         | NotifyFilters.FileName | NotifyFilters.DirectoryName;
  watcher.Filter = "*.*";
  watcher.Changed += OnChanged;
  watcher.EnableRaisingEvents = true;
}

private void OnChanged(object source, FileSystemEventArgs e)
{
  //Copies file to another directory.
}

public void Dispose()
{
  // avoiding resource leak
  watcher.Changed -= OnChanged;
  this.watcher.Dispose();
}
cheeseman
źródło

Odpowiedzi:

144

Problem polegał na filtrach powiadomień. Program próbował otworzyć plik, który wciąż kopiował. Usunąłem wszystkie filtry powiadomień z wyjątkiem LastWrite.

private void watch()
{
  FileSystemWatcher watcher = new FileSystemWatcher();
  watcher.Path = path;
  watcher.NotifyFilter = NotifyFilters.LastWrite;
  watcher.Filter = "*.*";
  watcher.Changed += new FileSystemEventHandler(OnChanged);
  watcher.EnableRaisingEvents = true;
}
cheeseman
źródło
6
Cześć, korzystałem z tego podejścia, ale kiedy kopiuję plik, zdarzenie jest wywoływane dwukrotnie: raz, gdy plik jest tworzony jako pusty (rozpoczyna się kopiowanie) i jeszcze raz, gdy kopiowanie się kończy. Jak uniknąć tego zduplikowanego zdarzenia, aby każdy filtr był w stanie je obsłużyć bez niestandardowej kontroli?
dhalfageme
@dhalfageme Sprawdzam oba zdarzenia, jeśli w folderze pojawi się coś istotnego dla mojej aplikacji.
Eftekhari
30

Nie dostarczyłeś kodu obsługi pliku, ale zakładam, że popełniłeś ten sam błąd, który każdy popełnia podczas pierwszego pisania czegoś takiego: zdarzenie filewatcher zostanie wywołane zaraz po utworzeniu pliku. Jednak ukończenie pliku zajmie trochę czasu. Weźmy na przykład rozmiar pliku 1 GB. Plik może zostać utworzony przez inny program (Explorer.exe kopiujący go skądś), ale zakończenie tego procesu zajmie kilka minut. Zdarzenie jest wywoływane w czasie tworzenia i musisz poczekać, aż plik będzie gotowy do skopiowania.

Możesz poczekać, aż plik będzie gotowy, używając tej funkcji w pętli.

nvoigt
źródło
25

Przyczyną może być to, że watcher jest zadeklarowana jako zmienna lokalna metody i jest usuwana z pamięci po zakończeniu metody. Powinieneś zadeklarować go jako członka klasy. Spróbuj wykonać następujące czynności:

FileSystemWatcher watcher;

private void watch()
{
  watcher = new FileSystemWatcher();
  watcher.Path = path;
  watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
                         | NotifyFilters.FileName | NotifyFilters.DirectoryName;
  watcher.Filter = "*.*";
  watcher.Changed += new FileSystemEventHandler(OnChanged);
  watcher.EnableRaisingEvents = true;
}

private void OnChanged(object source, FileSystemEventArgs e)
{
  //Copies file to another directory.
}
user1912419
źródło
18
watcherzmienna jest utrzymywana (nie zbierana), ponieważ zasubskrybował wydarzenie Changed.
adospace
1
Uważam, że dzieje się tak, ponieważ EnableRaisingEvents jest ustawione na true. Nie sądzę, aby status programów obsługi zdarzeń członkowskich miał coś wspólnego z wyrzucaniem elementów bezużytecznych. Myślę, że EnableRaisingEvents ma, co mógłbym nazwać, szczególne zachowanie w tym przypadku.
Matias Grioni