2009-07-02 1 views
12

Qualcun altro è la creazione di un file CSV aggiungendo una riga alla volta per esso, come si verificano gli eventi. Non ho alcun controllo sul formato del file o sull'altro processo, ma so che verrà aggiunto solo.In Java, qual è il modello migliore/più sicuro per il monitoraggio di un file aggiunto? processo

In un programma Java, vorrei monitorare questo file, e quando una linea viene aggiunto leggere la nuova linea e reagire in base al contenuto. Ignora il problema di analisi CSV per ora. Qual è il modo migliore per monitorare il file per le modifiche e leggere una riga alla volta?

Idealmente questo utilizzerà le classi della libreria standard. Il file potrebbe trovarsi su un'unità di rete, quindi mi piacerebbe qualcosa di solido in caso di errore. Preferisco non utilizzare il polling, se possibile - preferirei invece una soluzione di blocco.

Edit - dato che una soluzione di blocco non è possibile con le classi standard (grazie per la risposta), qual è la soluzione più robusta sondaggi? Preferirei non rileggere l'intero file ogni volta che potrebbe diventare abbastanza grande.

risposta

7

Dal Java 7 non v'è stato il metodo newWatchService() sul FileSystem class.

Tuttavia, ci sono alcune avvertenze:

  • è solo Java 7
  • Si tratta di un metodo facoltativo
  • è orologi solo directory, in modo da avere a che fare il lavoro di gestione dei file, e la preoccupazione sul file in movimento ecc.

Prima di Java 7 non è possibile con le API standard.

Ho provato quanto segue (polling su un intervallo di 1 sec) e funziona (si limita a stampare in lavorazione):

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

Come nessun altro ha suggerito una soluzione che utilizza una produzione attuale di Java ho pensato Lo aggiungerei. Se ci sono difetti si prega di aggiungere commenti.

+0

I codici precedenti garantiscono la lettura della riga aggiunta? – DerekY

+1

Il mio requisito è quello di guardare una cartella e non appena un file viene aggiunto/scritto/spostato nella cartella, eseguire immediatamente un'azione (ad esempio, inviare via email al file). Il problema che sto incontrando è che quando il file è grande, potrebbe essere necessario un po 'di tempo per essere completato scritto o copiato, mentre l'evento FILE_CREATE viene annunciato non appena i primi byte del file vengono scritti nella cartella. Quindi non posso eseguire immediatamente l'azione. Qual è un modo affidabile per determinare se il file è stato completamente scritto prima di eseguire qualsiasi azione su di esso? –

0

Probabilmente non servirà, basta pensare ad alta voce, ma su UNIX si userebbe tail -f per vedere qualsiasi riga aggiunta a un file - è necessario qualcosa di simile ma in termini di classi java. tail -f è essa stessa implementata con il polling, credo. Incontra EOF, quindi attende una piccola quantità di tempo (100ms), quindi prova a leggere di nuovo fino a EOF. Quindi ottiene sempre gli ultimi dati, perché mentre altri processi scrivono: EOF avanza.

2

questo non è possibile con le classi della libreria standard. Vedi questo question per i dettagli.

Per polling efficiente che sarà meglio usare Random Access. Aiuterà se ricordi la posizione dell'ultima parte del file e inizi a leggere da lì.

+0

Grazie - come ho modificato la domanda per riflettere, questo implica che ho bisogno di una soluzione di polling. Avete suggerimenti su ciò che è più robusto/efficiente? –

3

Usa Java 7 di WatchService, parte di NIO.2

L'API WatchService è progettato per le applicazioni che necessitano di notifica di eventi di modifica del file.

+1

Wow, è stato rilasciato Java 7? Devo partire in una grotta per un bel po 'di tempo. – kgiannakakis

+0

Attualmente è disponibile la versione di anteprima di accesso anticipato o l'ultima versione di snapshot binario. –

+2

WatchService guarda le directory, non i file – finnw

2

Solo per espandere l'ultima voce di Nick Fortescue, sotto sono due classi che è possibile eseguire contemporaneamente (ad esempio in due finestre di shell differenti) che mostrano che un determinato file può essere scritto contemporaneamente da un processo e letto da un altro.

Qui, i due processi eseguiranno queste classi Java, ma presumo che il processo di scrittura possa provenire da qualsiasi altra applicazione. (Supponendo che non sia in possesso di un blocco esclusivo sul file) ci sono blocchi del file system su determinati sistemi operativi?)

Ho testato con successo queste due classi su Windoze e Linux. Mi piacerebbe molto sapere se c'è qualche condizione (ad es. Sistema operativo) in cui falliscono.

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

Esecuzione di questi due lavori separatamente per me, ma se eseguo solo FileMonitor e modifica manualmente file.txt con vim, le modifiche non vengono riconosciute. Pensieri? –

1

Purtroppo, classe TailInputStream, che può essere utilizzato per monitorare la fine di un file, non è una delle piattaforme standard di Java classi, ma ci sono poche implementazioni sul web. È possibile trovare un'implementazione della classe TailInputStream insieme a un esempio di utilizzo su http://www.greentelligent.com/java/tailinputstream.

5

È possibile registrarsi per ricevere una notifica dal file system se si verificano modifiche al file utilizzando la classe WatchService. Ciò richiede Java7, qui il link per la documentazione http://docs.oracle.com/javase/tutorial/essential/io/notification.html

qui il frammento di codice per farlo:

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

Puoi modificare la tua risposta per dire che: questo è nuovo in Java 7, è in java.nio, e quel nuovoWatchService() è un metodo opzionale. Forse aggiungi un link al javadoc? –

0

sondaggio, sia su un ciclo coerente o su un ciclo random; 200-2000 ms dovrebbero essere una buona durata dell'intervallo casuale.

Controlla due cose ...

Se si deve guardare per la crescita di file, quindi controllare il numero di EOF/di byte, ed essere sicuri di confrontare i tempi che e FileAccess o FileWrite con il sondaggio lass. Se (>), il file è stato scritto.

Quindi, combinare quello con il controllo di accesso esclusivo di blocco/lettura. Se il file può essere bloccato in lettura ed è cresciuto, allora tutto ciò che è stato scritto è finito.

Il controllo di entrambe le proprietà non consente di ottenere uno stato di scrittura garantito ++ e effettivamente eseguito e disponibile per l'uso.