Listener di file modificato in Java


104

Vorrei essere avvisato quando un file è stato modificato nel file system. Non ho trovato altro che un thread che esegue il polling della proprietà lastModified File e chiaramente questa soluzione non è ottimale.


Grazie per tutte le risposte. Dato che le mie esigenze sono piccole (devo solo ricaricare un file di proprietà modificato nella mia applicazione web .. principalmente perché il riavvio dell'intera webapp è piuttosto lento), rimarrò fedele al modello di sondaggio. Almeno ora so che non esiste una soluzione semplice per questi casi.
cheng81

14
Solo una nota. Quando abbiamo risolto questo problema in precedenza, abbiamo scoperto che i file di grandi dimensioni tendono ad arrivare lentamente e il meccanismo di polling spesso rilevava un file nuovo o modificato prima che fosse completamente scritto. Per risolvere questo problema è stata adottata una soluzione "a due morsi". Il poller ha notato che un file era cambiato, ma non ha notificato al sistema un file nuovo / modificato fino a quando non sembrava lo stesso per due sondaggi: cioè si era stabilizzato. Ha risolto molti errori di file danneggiati.
Steve Powell

1
Per inciso, Tomcat soffre di questo problema quando rilascia file WAR di grandi dimensioni nella cartella webapps da fonti remote e lo ha fatto per molto tempo.
John Rix

Risposte:


21

Al livello basso l'unico modo per modellare questa utility è avere un thread che esegue il polling su una directory e tiene sotto controllo gli attributi del file. Ma puoi usare modelli per sviluppare un adattatore per tale utilità.

Ad esempio, i server delle applicazioni j2ee come Tomcat e altri hanno una funzione di caricamento automatico in cui non appena cambia il descrittore di distribuzione o cambia la classe servlet, l'applicazione si riavvia.

È possibile utilizzare le librerie di tali server poiché la maggior parte del codice di Tomcat è riutilizzabile e opensource.


59
Questo non è più vero in Java 7: ora c'è un'API per questo che può connettersi
Arnout Engelen

Quell'API è altamente inadeguata, non fornisce notifiche per gli eventi di chiusura dei file su Linux. Quindi, dato un caso semplice, quando un file viene chiuso, copiarlo in un'altra directory non funziona.
binarytemple_picsolve

117

Ho già scritto un monitor di file di registro in precedenza e ho scoperto che l'impatto sulle prestazioni del sistema del polling degli attributi di un singolo file, alcune volte al secondo, è in realtà molto piccolo.

Java 7, come parte di NIO.2, ha aggiunto l' API WatchService

L'API WatchService è progettata per le applicazioni che devono essere informate sugli eventi di modifica dei file.


10
vedo gli esempi come directory di controllo, ma per quanto riguarda un singolo file?
Archimedes Trajano

@ArchimedesTrajano L'API ti avvisa quando un file in una directory è cambiato. L'evento attivato include il nome del file che è stato modificato. Quindi puoi gestire eventi per un determinato file o file e ignorarne altri.
Michael



27

C'è una libreria chiamata jnotify che esegue il wrapping di inotify su Linux e ha anche il supporto per Windows. Non l'ho mai usato e non so quanto sia buono, ma vale la pena provare direi.


1
Funziona perfettamente e semplicissimo da usare. Uso inotify da molti anni. È veloce, stabile e affidabile
oᴉɹǝɥɔ

8

Java commons-io ha un FileAlterationObserver . esegue il polling in combinazione con un FileAlterationMonitor. Simile a Common VFS. Il vantaggio è che ha molte meno dipendenze.

modifica: Meno dipendenze non è vero, sono opzionali per VFS. Ma usa il file java invece del livello di astrazione VFS.


4

"Altre funzionalità NIO" ha funzionalità di controllo dei file, con l'implementazione che dipende dal sistema operativo sottostante. Dovrebbe essere in JDK7.


Potresti essere più specifico o pubblicare un collegamento alla documentazione? Non riesco a trovare nulla su questo ...
Luciano

Sembra essere il pacchetto java.nio.file. Guarda un tutorial .
David L.

4

Eseguo questo frammento di codice ogni volta che vado a leggere il file delle proprietà, leggendo il file solo se è stato modificato dall'ultima volta che l'ho letto. Spero che questo aiuti qualcuno.

private long timeStamp;
private File file;

private boolean isFileUpdated( File file ) {
  this.file = file;
  this.timeStamp = file.lastModified();

  if( this.timeStamp != timeStamp ) {
    this.timeStamp = timeStamp;
    //Yes, file is updated
    return true;
  }
  //No, file is not updated
  return false;
}

Un approccio simile viene utilizzato in Log4J FileWatchdog.


2

È possibile ascoltare le modifiche ai file utilizzando un FileReader. Per favore guarda l'esempio sotto

// File content change listener 
private String fname;
private Object lck = new Object();
... 
public void run()
{
    try
    {
        BufferedReader br = new BufferedReader( new FileReader( fname ) );
        String s;
        StringBuilder buf = new StringBuilder();
        while( true )
        {
            s = br.readLine();
            if( s == null )
            {
                synchronized( lck )
                {
                    lck.wait( 500 );
                }
            }
            else
            {
               System.out.println( "s = " + s );
            }

        }
    }
    catch( Exception e )
    {
        e.printStackTrace();
    }
}

2

Se sei disposto a separarti con un po 'di soldi, JNIWrapper è una libreria utile con un Winpack, sarai in grado di ottenere eventi del file system su determinati file. Purtroppo solo Windows.

Vedi https://www.teamdev.com/jniwrapper .

Altrimenti, il ricorso al codice nativo non è sempre una cosa negativa, soprattutto quando il meglio in offerta è un meccanismo di polling rispetto a un evento nativo.

Ho notato che le operazioni del file system Java possono essere lente su alcuni computer e possono facilmente influire sulle prestazioni dell'applicazione se non gestite bene.


1

Puoi anche considerare Apache Commons JCI (Java Compiler Interface). Sebbene questa API sembri essere focalizzata sulla compilazione dinamica di classi, include anche classi nella sua API che monitora le modifiche ai file.

Esempio: http://commons.apache.org/jci/usage.html




1

Il polling dell'ultima proprietà del file modificato è una soluzione semplice ma efficace. Basta definire una classe che estenda my FileChangedWatchere implementare il onModified()metodo:

import java.io.File;

public abstract class FileChangedWatcher
{
    private File file;

    public FileChangedWatcher(String filePath)
    {
        file = new File(filePath);
    }

    public void watch() throws InterruptedException
    {
        long currentModifiedDate = file.lastModified();

        while (true)
        {
            long newModifiedDate = file.lastModified();

            if (newModifiedDate != currentModifiedDate)
            {
                currentModifiedDate = newModifiedDate;
                onModified();
            }

            Thread.sleep(100);
        }
    }

    public String getFilePath()
    {
        return file.getAbsolutePath();
    }

    protected abstract void onModified();
}

0

Simile alle altre risposte, ecco come l'ho fatto usando File, Timer e TimerTask per farlo funzionare come un thread di polling in background a intervalli prestabiliti.

import java.io.File;
import java.util.Timer;
import java.util.TimerTask;

public class FileModifiedWatcher
{
  private static File file;
  private static int pollingInterval;
  private static Timer fileWatcher;
  private static long lastReadTimeStamp = 0L;

  public static boolean init(String _file, int _pollingInterval)
  {
    file =  new File(_file);
    pollingInterval = _pollingInterval; // In seconds

    watchFile();

    return true;
  }

  private static void watchFile()
  {
    if ( null == fileWatcher )
    {
      System.out.println("START");

      fileWatcher = new Timer();

      fileWatcher.scheduleAtFixedRate(new TimerTask()
      {
        @Override
        public void run()
        {

          if ( file.lastModified() > lastReadTimeStamp )
          {
            System.out.println("File Modified");
          }

          lastReadTimeStamp = System.currentTimeMillis();
        }
      }, 0, 1000 * pollingInterval);
    }

  }
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.