2009-07-02 9 views
12

Proces kogoś innego tworzy plik CSV, dodając do niego linię naraz, w miarę jak zdarzają się zdarzenia. Nie mam kontroli nad formatem pliku lub innym procesem, ale wiem, że to się tylko dopełni.W języku Java, jaki jest najlepszy/najbezpieczniejszy wzorzec monitorowania dołączanego pliku?

W programie Java chciałbym monitorować ten plik, a po dołączeniu linii odczytać nową linię i reagować zgodnie z zawartością. Zignoruj ​​na razie problem z analizą CSV. Jaki jest najlepszy sposób monitorowania pliku pod kątem zmian i czytania linii w czasie?

Idealnie byłoby użyć standardowych klas biblioteki. Plik może znajdować się na dysku sieciowym, więc chciałbym coś solidnego do awarii. Wolałbym raczej nie używać sondowania, jeśli to możliwe - wolałbym zamiast tego jakieś rozwiązanie blokujące.

Edycja - biorąc pod uwagę, że rozwiązanie blokujące nie jest możliwe w przypadku klas standardowych (dzięki za tę odpowiedź), jakie jest najsolidniejsze rozwiązanie ankietowe? Wolałbym nie czytać całego pliku za każdym razem, ponieważ może on być dość duży.

Odpowiedz

7

Od wersji Java 7 została wprowadzona metoda newWatchService() na urządzeniu FileSystem class.

Istnieją jednak pewne zastrzeżenia:

  • to tylko Java 7
  • Jest to opcjonalna metoda
  • tylko zegarki katalogów, więc trzeba zrobić plik magazynowe siebie i martwić o przenoszeniu plików itp.

Przed wersją 7 Java nie jest możliwa ze standardowymi interfejsami API.

Próbowałem następujące (odpytywanie w odstępie 1 sekundy) i działa (tylko drukuje w przetwórstwie):

private static void monitorFile(File file) throws IOException { 
    final int POLL_INTERVAL = 1000; 
    FileReader reader = new FileReader(file); 
    BufferedReader buffered = new BufferedReader(reader); 
    try { 
     while(true) { 
     String line = buffered.readLine(); 
     if(line == null) { 
      // end of file, start polling 
      Thread.sleep(POLL_INTERVAL); 
     } else { 
      System.out.println(line); 
     } 
     } 
    } catch(InterruptedException ex) { 
    ex.printStackTrace(); 
    } 
    } 

Jak nikt inny nie zaproponował rozwiązanie, które korzysta z bieżącej produkcji Java myślałem Dodałbym to. Jeśli są błędy, dodaj komentarze.

+0

Powyższe kody gwarantują czytanie dołączonej linii? – DerekY

+1

Moim wymaganiem jest oglądanie folderu i jak tylko plik zostanie dodany/zapisany/przeniesiony do folderu, podejmij natychmiastowe działanie (np. Przesyłając plik pocztą e-mail). Problem polega na tym, że gdy plik jest duży, może upłynąć trochę czasu, zanim zostanie ukończony zapisany lub skopiowany, podczas gdy zdarzenie FILE_CREATE zostanie ogłoszone, gdy tylko pierwsze bajty pliku zostaną zapisane w folderze. Nie mogę więc natychmiast wykonać tej akcji. Jaki jest niezawodny sposób sprawdzenia, czy plik został w całości zapisany przed wykonaniem jakiejkolwiek czynności na nim? –

0

Prawdopodobnie nie pomoże, po prostu myśląc głośno, ale w systemie UNIX można użyć tail -f , aby zobaczyć wszystkie linie dodane do pliku - potrzebujesz czegoś podobnego, ale pod względem klas java. tail -f sam jest implementowany z sondowaniem, jak sądzę. Napotyka EOF, następnie czeka trochę czasu (100 ms), a następnie próbuje ponownie odczytać do EOF. Tak więc zawsze dostaje najnowsze dane, ponieważ podczas innych procesów pisze - EOF porusza się do przodu.

2

Nie jest to możliwe w przypadku standardowych klas bibliotecznych. Szczegółowe informacje można znaleźć w tym numerze: question.

Dla efektywnego odpytywania lepiej będzie użyć Random Access. Pomoże to zapamiętać położenie ostatniego końca pliku i rozpocząć czytanie od tego miejsca.

+0

Dziękuję - ponieważ zredagowałem to pytanie do rozważenia, oznacza to, że potrzebuję rozwiązania do głosowania. Czy masz jakieś sugestie na temat tego, co jest najbardziej niezawodne/wydajne? –

3

Use Java 7 na WatchService część NIO.2

WatchService API jest przeznaczony dla aplikacji, które muszą zostać powiadomieni o imprezach modyfikacji pliku.

+1

Wow, czy Java 7 została wydana? Przez jakiś czas muszę wyjeżdżać do jaskini. – kgiannakakis

+0

Obecnie dostępna jest wersja z wcześniejszym dostępem do podglądu lub najnowsza wersja migawki binarnej. –

+2

Katalogi zegarków WatchService, a nie pliki – finnw

2

Aby rozwinąć ostatni wpis Nicka Fortescue, poniżej znajdują się dwie klasy, które można uruchamiać jednocześnie (np. W dwóch różnych oknach powłoki), co pokazuje, że dany plik może być jednocześnie zapisywany jednym procesem i czytany przez inny.

Tutaj dwa procesy będą wykonywać te klasy Java, ale przypuszczam, że proces pisania mógłby pochodzić z dowolnej innej aplikacji. (Zakładając, że nie ma wyłącznej blokady pliku - czy są jakieś blokady systemu plików w niektórych systemach operacyjnych?)

Z powodzeniem przetestowałem te dwie klasy zarówno w trybie Windoze, jak i Linux. Bardzo chciałbym wiedzieć, czy jest jakiś warunek (na przykład system operacyjny), na którym zawodzą.

klasa # 1:

import java.io.File; 
import java.io.FileWriter; 
import java.io.PrintWriter; 

public class FileAppender { 

    public static void main(String[] args) throws Exception { 
     if ((args != null) && (args.length != 0)) throw 
      new IllegalArgumentException("args is not null and is not empty"); 

     File file = new File("./file.txt"); 
     int numLines = 1000; 
     writeLines(file, numLines); 
    } 

    private static void writeLines(File file, int numLines) throws Exception { 
     PrintWriter pw = null; 
     try { 
      pw = new PrintWriter(new FileWriter(file), true); 
      for (int i = 0; i < numLines; i++) { 
       System.out.println("writing line number " + i); 
       pw.println("line number " + i); 
       Thread.sleep(100); 
      } 
     } 
     finally { 
      if (pw != null) pw.close(); 
     } 
    } 

} 

Class # 2:

import java.io.BufferedReader; 
import java.io.File; 
import java.io.FileReader; 

public class FileMonitor { 

    public static void main(String[] args) throws Exception { 
     if ((args != null) && (args.length != 0)) throw 
      new IllegalArgumentException("args is not null and is not empty"); 

     File file = new File("./file.txt"); 
     readLines(file); 
    } 

    private static void readLines(File file) throws Exception { 
     BufferedReader br = null; 
     try { 
      br = new BufferedReader(new FileReader(file)); 
      while (true) { 
       String line = br.readLine(); 
       if (line == null) { // end of file, start polling 
        System.out.println("no file data available; sleeping.."); 
        Thread.sleep(2 * 1000); 
       } 
       else { 
        System.out.println(line); 
       } 
      } 
     } 
     finally { 
      if (br != null) br.close(); 
     } 
    } 

} 
+0

Uruchamianie tych dwóch osobno dla mnie działa, ale jeśli uruchomię tylko FileMonitor i ręcznie edytuję plik file.txt w vim, zmiany nie zostaną rozpoznane. Myśli? –

1

Niestety, klasa TailInputStream, który może być używany do monitorowania końcu pliku, nie jest jednym z standardowej platformie Java klasy, ale w Internecie jest niewiele implementacji. Możesz znaleźć implementację klasy TailInputStream wraz z przykładem użycia na http://www.greentelligent.com/java/tailinputstream.

5

Możesz się zarejestrować, aby otrzymać powiadomienie przez system plików, jeśli nastąpi jakakolwiek zmiana w pliku przy użyciu klasy WatchService. Wymaga Java7, tutaj link do dokumentacji http://docs.oracle.com/javase/tutorial/essential/io/notification.html

tutaj fragment kodu, aby to zrobić:

public FileWatcher(Path dir) { 
    this.watcher = FileSystems.getDefault().newWatchService(); 
    WatchKey key = dir.register(watcher, ENTRY_MODIFY); 
} 

void processEvents() { 
    for (;;) { 
     // wait for key to be signalled 
     WatchKey key; 
     try { 
      key = watcher.take(); 
     } catch (InterruptedException x) { 
      return; 
     } 

     for (WatchEvent<?> event : key.pollEvents()) { 
      WatchEvent.Kind<?> kind = event.kind(); 

      if (kind == OVERFLOW) { 
       continue; 
      } 
      // Context for directory entry event is the file name of entry 
      WatchEvent<Path> ev = cast(event); 
      Path name = ev.context(); 
      Path child = dir.resolve(name); 
      // print out event 
      System.out.format("%s: %s file \n", event.kind().name(), child); 
     } 
     // reset key and remove from set if directory no longer accessible 
     boolean valid = key.reset(); 
    } 
} 
+1

Czy możesz edytować odpowiedź, aby powiedzieć, że: jest nowy w Java 7, jest w java.nio, a newWatchService() jest opcjonalną metodą. Może dodać link do javadoc? –

0

Poll, albo w sposób konsekwentny cyklu lub na losowej cyklu; 200-2000 ms powinno być dobrym, losowym przedziałem czasowym odpytywania.

Sprawdź dwie rzeczy ...

Jeśli trzeba uważać na wzrost pliku, a następnie sprawdzić licznik EOF/bajt, i mieć pewność, że i porównać czasy fileAccess lub FILEWRITE z Lass sondzie. Jeśli (>), to plik został zapisany.

Następnie połącz to ze sprawdzeniem wyłącznego dostępu do blokowania/odczytu. Jeśli plik może być zablokowany i wzrósł, skończyło się to, co do niego napisano.

Sprawdzanie, czy dana nieruchomość jest właściwa, niekoniecznie zapewni Ci gwarantowany stan napisany ++ i faktycznie wykonany i dostępny do użytku.