2009-07-02 9 views
12

Der Prozess eines anderen Benutzers erstellt eine CSV-Datei, indem bei Auftreten von Ereignissen jeweils eine Zeile an sie angehängt wird. Ich habe keine Kontrolle über das Dateiformat oder den anderen Prozess, aber ich weiß, dass es nur anhängen wird.In Java, welches ist das beste/sicherste Muster für die Überwachung einer Datei, an die angehängt wird?

In einem Java-Programm möchte ich diese Datei überwachen, und wenn eine Zeile angehängt ist, lesen Sie die neue Zeile und reagieren Sie entsprechend dem Inhalt. Ignorieren Sie das CSV-Analyseproblem vorerst. Was ist der beste Weg, um die Datei auf Änderungen zu überwachen und Zeile für Zeile zu lesen?

Idealerweise verwendet dies die Standardbibliotheksklassen. Die Datei befindet sich möglicherweise auf einem Netzlaufwerk, daher möchte ich etwas Robustes bis zum Ausfall haben. Ich würde lieber keine Umfragen verwenden, wenn möglich - ich würde stattdessen eine Art Blockierungslösung bevorzugen.

Bearbeiten - da eine Blockierungslösung mit Standardklassen nicht möglich ist (danke für diese Antwort), was ist die robusteste Polling-Lösung? Ich würde lieber nicht die ganze Datei jedes Mal neu lesen, da sie ziemlich groß werden könnte.

Antwort

7

Seit Java 7 gibt es die newWatchService() Methode auf der FileSystem class.

Allerdings gibt es einige Einschränkungen:

  • Es ist nur Java 7
  • Es ist eine optionale Methode
  • es nur Verzeichnisse Uhren, so dass Sie die Datei Umgang mit sich selbst zu tun haben, und Sorge über die Datei verschieben usw.

Vor Java 7 ist es nicht möglich mit Standard-APIs.

ich folgendes (Polling auf einem 1 sec Intervall) ausprobiert und es funktioniert (druckt nur in der Verarbeitung):

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(); 
    } 
    } 

Da sonst niemand eine Lösung vorgeschlagen hat, die eine aktuelle Produktion verwendet Java Ich dachte, Ich würde es hinzufügen. Wenn es Mängel gibt, fügen Sie bitte Kommentare hinzu.

+0

Die obigen Codes garantieren das Lesen der angehängten Zeile? – DerekY

+1

Meine Anforderung ist es, einen Ordner zu sehen und sobald eine Datei hinzugefügt/geschrieben/in den Ordner verschoben wurde, ergreifen Sie sofort eine Aktion (z. B. um die Datei per E-Mail zu versenden). Problem ist, dass es, wenn die Datei groß ist, eine Weile dauern kann, bis sie fertig geschrieben oder kopiert ist, während das FILE_CREATE-Ereignis angesagt wird, sobald die ersten Bytes der Datei in den Ordner geschrieben werden. Daher kann ich die Aktion nicht sofort ausführen. Was ist eine zuverlässige Methode, um festzustellen, ob die Datei vollständig geschrieben wurde, bevor eine Aktion ausgeführt wird? –

0

wird wahrscheinlich nicht helfen, nur laut gedacht, aber auf UNIX würde man tail -f verwendet alle Zeilen in eine Datei hinzugefügt, um zu sehen - Sie etwas ähnlich, aber in Bezug auf Java-Klassen müssen. Tail -f ist selbst mit Polling implementiert glaube ich. Es stößt auf EOF, wartet dann einige Zeit (100ms) und versucht dann erneut bis EOF zu lesen. So erhält es immer die neuesten Daten, denn während andere Prozesse schreiben - EOF bewegt sich vorwärts.

2

Dies ist mit Standardbibliotheksklassen nicht möglich. Siehe hierzu question für Details.

Für eine effiziente Abfrage wird es besser sein, Random Access zu verwenden. Es hilft Ihnen, wenn Sie sich an die Position des letzten Dateiendes erinnern und von dort aus lesen.

+0

Danke - wie ich die Frage bearbeitet habe, um zu reflektieren, bedeutet dies, dass ich eine Polling-Lösung brauche. Haben Sie Vorschläge, was am robustesten/effizientesten ist? –

3

Verwenden Java 7s WatchService, Teil NIO.2

Die WatchService API ist für Anwendungen, die über Dateiänderungsereignisse benachrichtigt werden müssen.

+1

Wow, ist Java 7 veröffentlicht? Ich muss eine ganze Weile in einer Höhle sein. – kgiannakakis

+0

Momentan gibt es entweder die Vorschau der frühen Zugriffe oder die neueste binäre Snapshot-Version. –

+2

WatchService überwacht Verzeichnisse, keine Dateien – finnw

2

Um den letzten Eintrag von Nick Fortescue zu erweitern, gibt es zwei Klassen, die gleichzeitig ausgeführt werden können (z. B. in zwei Shellfenstern), die zeigen, dass eine Datei gleichzeitig von einem Prozess beschrieben und von einem anderen gelesen werden kann.

Hier werden die beiden Prozesse diese Java-Klassen ausführen, aber ich nehme an, dass der Schreibprozess von jeder anderen Anwendung stammen könnte. (Angenommen, es enthält keine exklusive Sperre für die Datei - gibt es solche Dateisystemsperren für bestimmte Betriebssysteme?)

Ich habe diese beiden Klassen erfolgreich auf Windoze und Linux getestet. Ich würde sehr gerne wissen, ob es eine Bedingung (z. B. Betriebssystem) gibt, bei der sie fehlschlagen.

Klasse # 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(); 
     } 
    } 

} 

Klasse # 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

Das Ausführen dieser beiden Dateien funktioniert für mich, aber wenn ich nur FileMonitor starte und file.txt manuell mit vim bearbeite, werden die Änderungen nicht erkannt. Gedanken? –

1

Leider TailInputStream Klasse, die verwendet werden kann, um das Ende einer Datei zu überwachen, ist nicht einer von Standard-Java-Plattform Klassen, aber es gibt nur wenige Implementierungen im Internet. Sie können eine Implementierung der TailInputStream-Klasse zusammen mit einem Verwendungsbeispiel unter http://www.greentelligent.com/java/tailinputstream finden.

5

Sie können sich registrieren, um vom Dateisystem benachrichtigt zu werden, wenn Änderungen an der Datei mit der WatchService-Klasse vorgenommen werden. Dies erfordert Java7, hier den Link für die Dokumentation http://docs.oracle.com/javase/tutorial/essential/io/notification.html

hier der Snippet-Code zu tun, dass:

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

Können Sie Ihre Antwort bearbeiten, um zu sagen, dass dies in Java 7 neu ist, es in java.nio ist und dass newWatchService() eine optionale Methode ist. Vielleicht einen Link zum Javadoc hinzufügen? –

0

Poll, entweder auf einem einheitlichen Zyklus oder an einem zufälligen Zyklus; 200-2000ms sollten eine gute zufällige Pollintervallspanne sein.

prüfen zwei Dinge ...

Wenn Sie Datei Wachstum beobachten haben, dann überprüfen Sie die EOF/Bytezählung und sicher sein, dass und die Dateiablage oder Filewrite mal mit dem Mädel Umfrage zu vergleichen. Wenn (>), wurde die Datei geschrieben.

Kombinieren Sie das dann mit der Prüfung auf exklusiven Sperr-/Lesezugriff. Wenn die Datei schreibgeschützt sein kann und gewachsen ist, ist alles, was darauf geschrieben wurde, beendet.

Die Prüfung auf eine der beiden Eigenschaften allein führt nicht unbedingt zu einem garantierten Zustand der geschriebenen ++ und tatsächlich erfolgt und verfügbar ist.