Plik zmienił nasłuchiwanie w Javie

104

Chciałbym otrzymać powiadomienie, gdy plik zostanie zmieniony w systemie plików. Nie znalazłem nic poza wątkiem, który sonduje właściwość lastModified File i najwyraźniej to rozwiązanie nie jest optymalne.

cheng81
źródło
Dzięki za wszystkie odpowiedzi. Ponieważ moje potrzeby są małe (potrzebuję tylko przeładować zmieniony plik właściwości w mojej aplikacji internetowej… głównie dlatego, że ponowne uruchamianie całej aplikacji internetowej jest trochę powolne), pozostanę przy modelu ankiety. Przynajmniej teraz wiem, że nie ma prostego rozwiązania dla tych przypadków.
cheng81
14
Tylko uwaga. Kiedy rozwiązaliśmy ten problem, zanim odkryliśmy, że duże pliki zwykle przychodzą powoli, a mechanizm odpytywania często odkrywał nowy lub zmieniony plik, zanim został w pełni zapisany. Aby rozwiązać ten problem, przyjęto rozwiązanie typu „dwa kęsy”. Ankieter zauważył, że plik uległ zmianie, ale nie powiadomił systemu o nowym / zmienionym pliku, dopóki nie wyglądał tak samo przez dwie ankiety, tj. Nie ustabilizował się. Wyleczono wiele błędnych plików.
Steve Powell
1
Nawiasem mówiąc, Tomcat ma ten problem podczas upuszczania dużych plików WAR do folderu webapps ze zdalnych źródeł i robi to przez długi czas.
John Rix

Odpowiedzi:

21

Na niskim poziomie jedynym sposobem modelowania tego narzędzia jest odpytywanie wątków w katalogu i obserwowanie atrybutów pliku. Ale możesz użyć wzorców, aby opracować adapter dla takiego narzędzia.

Na przykład serwery aplikacji j2ee, takie jak Tomcat i inne, mają funkcję automatycznego ładowania, która powoduje ponowne uruchomienie aplikacji po zmianie deskryptora wdrażania lub zmianie klasy serwletu.

Możesz korzystać z bibliotek z takich serwerów, ponieważ większość kodu tomcat jest wielokrotnego użytku i open source.

Rutesh Makhijani
źródło
59
To już nie jest prawdą w Javie 7: istnieje teraz interfejs API, który może podłączyć się do usług powiadamiania systemu operacyjnego: blogs.oracle.com/thejavatutorials/entry/ ...
Arnout Engelen
Ten interfejs API jest wysoce nieodpowiedni i nie zapewnia powiadomień o zdarzeniach związanych z zamknięciem plików w systemie Linux. Tak więc, biorąc pod uwagę prosty przypadek, gdy plik jest zamknięty, skopiowanie go do innego katalogu nie działa.
binarytemple_picsolve
117

Napisałem już wcześniej monitor pliku dziennika i odkryłem, że wpływ odpytywania atrybutów pojedynczego pliku kilka razy na sekundę na wydajność systemu jest w rzeczywistości bardzo mały.

Java 7, jako część NIO.2, dodała WatchService API

WatchService API jest przeznaczony dla aplikacji, które muszą być powiadamiane o zdarzeniach dotyczących zmiany plików.

Stephen Denne
źródło
10
widzę przykłady jako obserwację katalogu, ale co z pojedynczym plikiem?
Archimedes Trajano
@ArchimedesTrajano Interfejs API powiadamia o zmianie pliku w katalogu. Wyzwalane zdarzenie zawiera nazwę pliku, który się zmienił. Możesz więc obsługiwać zdarzenia dla określonego pliku lub plików i ignorować inne.
Michael
@ArchimedesTrajano stackoverflow.com/questions/16251273/ ...
user1742529
39

Używam API VFS z Apache Commons, oto przykład, jak monitorować plik bez większego wpływu na wydajność:

DefaultFileMonitor

Telcontar
źródło
27

Istnieje biblioteka o nazwie jnotify, która otacza inotify w systemie Linux i obsługuje również okna. Nigdy go nie używałem i nie wiem, jakie jest dobre, ale powiedziałbym, że warto spróbować.

André
źródło
1
Działa doskonale i jest super prosty w użyciu. Używam inotify od wielu lat. Jest szybki, stabilny i niezawodny
oᴉɹǝɥɔ
8

Java commons-io ma FileAlterationObserver . wykonuje odpytywanie w połączeniu z FileAlterationMonitor. Podobny do commons VFS. Zaletą jest to, że ma znacznie mniej zależności.

edycja: mniej zależności nie jest prawdą, są one opcjonalne dla VFS. Ale używa pliku java zamiast warstwy abstrakcji VFS.

chrześcijanin
źródło
4

„Więcej funkcji NIO” ma funkcję monitorowania plików, której implementacja zależy od bazowego systemu operacyjnego. Powinien być w JDK7.

Tom Hawtin - haczyk
źródło
Czy mógłbyś być bardziej szczegółowy lub zamieścić link do dokumentacji? Nie mogę znaleźć niczego na ten temat ...
Luciano,
Wydaje się, że to pakiet java.nio.file. Zobacz samouczek .
David L.
4

Uruchamiam ten fragment kodu za każdym razem, gdy czytam plik właściwości, w rzeczywistości czytam plik tylko wtedy, gdy został zmodyfikowany od czasu ostatniego czytania. Mam nadzieję, że to komuś pomoże.

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

Podobne podejście zastosowano w Log4J FileWatchdog.

Mike Kramer
źródło
2

Możesz słuchać zmian plików za pomocą FileReader. Plz zobacz poniższy przykład

// 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();
    }
}
Prasanna Sujeewa
źródło
2

Jeśli chcesz rozstać się z pewnymi pieniędzmi, JNIWrapper jest przydatną biblioteką z pakietem Winpack, będziesz w stanie uzyskać zdarzenia systemu plików w niektórych plikach. Niestety tylko okna.

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

W przeciwnym razie uciekanie się do kodu natywnego nie zawsze jest złą rzeczą, zwłaszcza gdy najlepszą ofertą jest mechanizm odpytywania, a nie zdarzenie natywne.

Zauważyłem, że operacje systemu plików Java mogą być powolne na niektórych komputerach i mogą łatwo wpływać na wydajność aplikacji, jeśli nie są dobrze obsługiwane.

jdh80
źródło
1

Możesz również rozważyć Apache Commons JCI (Java Compiler Interface). Chociaż wydaje się, że ten interfejs API koncentruje się na dynamicznej kompilacji klas, zawiera również klasy w swoim API, które monitorują zmiany plików.

Przykład: http://commons.apache.org/jci/usage.html

onejigtwojig
źródło
1

Sondowanie ostatnio zmodyfikowanej właściwości pliku jest jednak prostym, ale skutecznym rozwiązaniem. Po prostu zdefiniuj klasę rozszerzającą my FileChangedWatcheri zaimplementuj onModified()metodę:

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();
}
BullyWiiPlaza
źródło
0

Podobnie jak w przypadku innych odpowiedzi, oto jak to zrobiłem za pomocą File, Timer i TimerTask, aby uruchomić to jako sondowanie wątku w tle w ustalonych odstępach czasu.

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

  }
}
sjsperry
źródło