Mam aplikację, w której szukam pliku tekstowego i jeśli w pliku są jakieś zmiany, korzystam z modułu OnChanged
obsługi zdarzeń do obsługi zdarzenia. Korzystam z, NotifyFilters.LastWriteTime
ale zdarzenie jest uruchamiane dwukrotnie. Oto kod.
public void Initialize()
{
FileSystemWatcher _fileWatcher = new FileSystemWatcher();
_fileWatcher.Path = "C:\\Folder";
_fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
_fileWatcher.Filter = "Version.txt";
_fileWatcher.Changed += new FileSystemEventHandler(OnChanged);
_fileWatcher.EnableRaisingEvents = true;
}
private void OnChanged(object source, FileSystemEventArgs e)
{
.......
}
W moim przypadku OnChanged
wywoływany jest dwukrotnie, kiedy zmieniam plik tekstowy version.txt
i zapisuję go.
c#
filesystemwatcher
użytkownik214707
źródło
źródło
Odpowiedzi:
Obawiam się, że jest to dobrze znany błąd / funkcja
FileSystemWatcher
klasy. To jest z dokumentacji klasy:Teraz ten fragment tekstu dotyczy
Created
zdarzenia, ale to samo dotyczy również innych zdarzeń plików. W niektórych aplikacjach możesz obejść ten problem, używającNotifyFilter
właściwości, ale z mojego doświadczenia wynika, że czasami trzeba również wykonać ręczne filtrowanie duplikatów (włamania).Jakiś czas temu zarezerwowałem stronę z kilkoma wskazówkami FileSystemWatcher . Może będziesz chciał to sprawdzić.
źródło
„Naprawiłem” ten problem za pomocą następującej strategii u mojego delegata:
źródło
Wszelkie zduplikowane
OnChanged
zdarzenia zFileSystemWatcher
można wykryć i odrzucić, sprawdzającFile.GetLastWriteTime
znacznik czasu w danym pliku. Tak jak:źródło
"Rename"
nazwę wydarzenia, które Cię interesuje):Observable.FromEventPattern<FileSystemEventArgs>(fileSystemWatcher, "Renamed") .Select(e => e.EventArgs) .Distinct(e => e.FullPath) .Subscribe(onNext);
DateTime
ma tylko milisekundy rozdzielczość, metoda ta działa nawet jeśli zastąpiFile.GetLastWriteTime
sięDateTime.Now
. W zależności od sytuacji możesz także użyća.FullName
zmiennej globalnej w celu wykrycia zduplikowanych zdarzeń.Oto moje rozwiązanie, które pomogło mi zatrzymać wydarzenie dwukrotnie:
Tutaj ustawiłem
NotifyFilter
właściwość tylko z nazwą pliku i rozmiarem.watcher
jest moim obiektem FileSystemWatcher. Mam nadzieję, że to pomoże.źródło
Mój scenariusz jest taki, że mam maszynę wirtualną z serwerem Linux. Tworzę pliki na hoście Windows. Kiedy zmieniam coś w folderze na hoście, chcę, aby wszystkie zmiany zostały przesłane, zsynchronizowane z serwerem wirtualnym przez Ftp. W ten sposób eliminuję zduplikowane zdarzenie zmiany, gdy piszę do pliku (który oznacza również folder zawierający plik do zmodyfikowania):
Głównie tworzę hashtable do przechowywania informacji o czasie zapisu pliku. Następnie, jeśli tablica skrótów ma zmodyfikowaną ścieżkę pliku, a jej wartość czasu jest taka sama, jak w przypadku zmiany pliku, który jest aktualnie powiadamiany, to wiem, że to duplikat zdarzenia i zignoruj go.
źródło
ToString("o")
ale bądź przygotowany na więcej awarii.Spróbuj użyć tego kodu:
źródło
if (let==false) { ... } else { let = false; }
? Niesamowite, jak to się zyskało, to tylko kwestia odznak StackOverflow.Oto moje podejście:
Jest to rozwiązanie, którego użyłem do rozwiązania tego problemu w projekcie, w którym wysyłałem plik jako załącznik w wiadomości e-mail. Z łatwością pozwoli uniknąć podwójnie uruchomionego zdarzenia, nawet przy mniejszym interwale czasowym, ale w moim przypadku 1000 było w porządku, ponieważ byłem szczęśliwszy z pominięcia kilku zmian niż z zalania skrzynki pocztowej> 1 wiadomością na sekundę. Przynajmniej działa dobrze, jeśli kilka plików zostanie zmienionych w tym samym czasie.
Innym rozwiązaniem, o którym myślałem, byłoby zastąpienie listy plikami mapowania słownika do ich odpowiednich MD5, więc nie musiałbyś wybierać dowolnego interwału, ponieważ nie musiałbyś usuwać wpisu, ale aktualizować jego wartość, i anuluj swoje rzeczy, jeśli się to nie zmieniło. Ma to tę wadę, że Słownik rośnie w pamięci, ponieważ pliki są monitorowane i zjadają coraz więcej pamięci, ale czytałem gdzieś, że ilość monitorowanych plików zależy od bufora wewnętrznego FSW, więc może nie jest to tak ważne. Nie wiem, jak czas obliczeń MD5 wpłynie na wydajność twojego kodu, ostrożnie = \
źródło
lock (_changedFiles) { if (_changedFiles.Contains(e.FullPath)) { return; } _changedFiles.Add(e.FullPath); // add this! } // do your stuff
_changedFiles
Jest dostępne z wielu wątków. Jednym ze sposobów, aby to naprawić, jest użycieConcurrentDictionary
zamiastList
. Innym sposobem jest przypisanie prąduForm
doTimer.SynchronizingObject
właściwości, a także doFileSystemWatcher.SynchronizingObject
właściwości.Utworzyłem repozytorium Git z klasą, która rozciąga się
FileSystemWatcher
na wyzwalanie zdarzeń tylko po zakończeniu kopiowania. Odrzuca wszystkie zmienione zdarzenia oprócz ostatniego i podnosi je dopiero, gdy plik będzie dostępny do odczytu.Pobierz FileSystemSafeWatcher i dodaj go do swojego projektu.
Następnie użyj go jak normalnie
FileSystemWatcher
i monitoruj, kiedy zdarzenia zostaną wyzwolone.źródło
Wiem, że to stary problem, ale miałem ten sam problem i żadne z powyższych rozwiązań nie rozwiązało problemu, z którym miałem do czynienia. Utworzyłem słownik, który mapuje nazwę pliku za pomocą LastWriteTime. Jeśli więc pliku nie ma w słowniku, przejdziemy do następnego kroku, aby sprawdzić, kiedy był ostatnio modyfikowany czas i czy różni się od tego, co znajduje się w słowniku, uruchom kod.
źródło
your code here
sekcji powinieneś dodać lub zaktualizować dateTimeDictionary.dateTimeDictionary[e.FullPath] = System.IO.File.GetLastWriteTime(e.FullPath);
Jednym z możliwych „hacków” byłoby zdławienie zdarzeń za pomocą Reactive Extensions na przykład:
W tym przypadku dławię do 50 ms, w moim systemie to wystarczyło, ale wyższe wartości powinny być bezpieczniejsze. (I jak powiedziałem, to wciąż „hack”).
źródło
.Distinct(e => e.FullPath)
którym uważam, że jest bardziej intuicyjny w obsłudze. I przywrócono zachowanie, którego można oczekiwać od interfejsu API.Mam tutaj bardzo szybkie i proste obejście, działa dla mnie i bez względu na to, czy zdarzenie zostanie uruchomione raz, dwa lub więcej razy, sprawdź to:
źródło
Oto nowe rozwiązanie, które możesz wypróbować. Działa dla mnie dobrze. W module obsługi zdarzenia dla zmienionego zdarzenia programowo usuń moduł obsługi z wyjścia projektanta, w razie potrzeby, a następnie programowo dodaj moduł obsługi z powrotem. przykład:
źródło
this.fileSystemWatcher1.Changed -= this.fileSystemWatcher1_Changed;
powinien zrobić właściwą rzecz.Głównym powodem, dla którego ostatni raz dostęp do pierwszego zdarzenia był bieżący (czas zapisu lub zmiany pliku). drugim zdarzeniem był pierwotny czas ostatniego dostępu do pliku. Rozwiązuję pod kodem.
źródło
Spędziłem sporo czasu przy użyciu FileSystemWatcher, a niektóre z podejść tutaj nie będą działać. Bardzo podobało mi się podejście polegające na wyłączaniu zdarzeń, ale niestety nie działa, jeśli upuszczono> 1 plik, drugi plik zostanie pominięty najbardziej, jeśli nie cały czas. Dlatego stosuję następujące podejście:
źródło
Ten kod działał dla mnie.
źródło
głównie dla mnie przyszłości :)
Napisałem opakowanie za pomocą Rx:
Stosowanie:
źródło
Zmieniłem sposób monitorowania plików w katalogach. Zamiast używać FileSystemWatcher sonduję lokalizacje w innym wątku, a następnie patrzę na LastWriteTime pliku.
Korzystając z tych informacji i prowadząc indeks ścieżki pliku oraz czas ostatniego zapisu, mogę określić pliki, które uległy zmianie lub zostały utworzone w określonej lokalizacji. To usuwa mnie z osobliwości FileSystemWatcher. Główną wadą jest to, że potrzebujesz struktury danych do przechowywania LastWriteTime i odwołania do pliku, ale jest to niezawodne i łatwe do wdrożenia.
źródło
Możesz spróbować otworzyć go do zapisu, a jeśli się powiedzie, możesz założyć, że inna aplikacja została wykonana z plikiem.
Samo otwarcie go do zapisu wydaje się nie wywoływać zmienionego zdarzenia. Więc powinno być bezpiecznie.
źródło
źródło
Przepraszam za wykopaliska, ale od jakiegoś czasu walczę z tym problemem i wreszcie znalazłem sposób na poradzenie sobie z tymi kilkoma wystrzelonymi zdarzeniami. Chciałbym podziękować wszystkim w tym wątku, ponieważ użyłem go w wielu referencjach podczas walki z tym problemem.
Oto mój pełny kod. Używa słownika do śledzenia daty i godziny ostatniego zapisu pliku. Porównuje tę wartość, a jeśli jest taka sama, tłumi zdarzenia. Następnie ustawia wartość po rozpoczęciu nowego wątku.
źródło
Jeśli nie zostaniesz zapytany, szkoda, że nie ma gotowych próbek rozwiązania dla F #. Aby to naprawić, oto mój przepis, tylko dlatego, że mogę, a F # to wspaniały język .NET.
Zduplikowane zdarzenia są odfiltrowywane za pomocą
FSharp.Control.Reactive
pakietu, który jest tylko opakowaniem F # dla reaktywnych rozszerzeń. Wszystko to może być ukierunkowane na pełne ramy lubnetstandard2.0
:źródło
W moim przypadku muszę uzyskać ostatnią linię pliku tekstowego, który jest wstawiany przez inną aplikację, jak tylko wstawianie zostanie wykonane. Oto moje rozwiązanie. Kiedy pierwsze zdarzenie jest wywoływane, wyłączam obserwator od podnoszenia innych, a następnie wywołuję timer TimeElapsedEvent, ponieważ kiedy moja funkcja uchwytu OnChanged jest wywoływana, potrzebuję rozmiaru pliku tekstowego, ale rozmiar w tym czasie nie jest rzeczywistym rozmiarem, jest to rozmiar pliku bezpośrednio przed wstawieniem. Więc czekam chwilę, aby przejść do właściwego rozmiaru pliku.
źródło
Spróbuj tego, działa dobrze
źródło
Chciałem zareagować tylko na ostatnie zdarzenie, na wszelki wypadek, również przy zmianie pliku linux wydawało się, że plik był pusty przy pierwszym wywołaniu, a następnie wypełniany ponownie przy następnym, i nie miałem nic przeciwko stracie czasu na wypadek, gdyby system operacyjny postanowiłem dokonać zmiany pliku / atrybutu.
Korzystam z asynchronizacji platformy .NET, aby pomóc mi w tworzeniu wątków.
źródło
Myślę, że najlepszym rozwiązaniem do rozwiązania tego problemu jest użycie rozszerzeń reaktywnych Po przekształceniu zdarzenia w obserwowalne, możesz po prostu dodać Ograniczanie (..) (pierwotnie zwane Debounce (..))
Przykładowy kod tutaj
źródło
Byłem w stanie to zrobić, dodając funkcję, która sprawdza duplikaty w tablicy buforów.
Następnie wykonaj akcję po tym, jak tablica nie zostanie zmodyfikowana na czas X za pomocą timera: - Resetuj timer za każdym razem, gdy coś jest zapisywane w buforze - Wykonaj akcję po zaznaczeniu
Przechwytuje to także inny typ duplikacji. Jeśli zmodyfikujesz plik w folderze, folder zgłasza również zdarzenie Zmień.
źródło
To rozwiązanie działało dla mnie w aplikacji produkcyjnej:
Środowisko:
VB.Net Framework 4.5.2
Ustaw ręcznie właściwości obiektu: NotifyFilter = Rozmiar
Następnie użyj tego kodu:
źródło
Spróbuj tego!
źródło
Musiałem połączyć kilka pomysłów z powyższych postów i dodać kontrolę blokowania plików, aby działała dla mnie:
źródło
Podszedłem do problemu podwójnego tworzenia, który ignoruje pierwsze zdarzenie:
źródło