Ouvinte de alteração de arquivo em Java


104

Gostaria de ser notificado quando um arquivo for alterado no sistema de arquivos. Não encontrei nada além de um thread que pesquisa a propriedade lastModified File e, claramente, essa solução não é a ideal.


Obrigado por todas as respostas. Como minhas necessidades são pequenas (só preciso recarregar um arquivo de propriedade alterado no meu aplicativo da web ... principalmente porque reiniciar todo o webapp é meio lento), vou ficar com o modelo de pesquisa. Pelo menos agora sei que não existe uma solução simples para esses casos.
cheng81

14
Apenas uma nota. Quando resolvemos esse problema antes, descobrimos que arquivos grandes tendem a chegar lentamente e o mecanismo de pesquisa frequentemente descobria um arquivo novo ou alterado antes de ser totalmente escrito. Para resolver isso, foi adotada uma solução de 'duas mordidas'. O pesquisador percebeu que um arquivo havia sido alterado, mas não notificou o sistema sobre um arquivo novo / alterado até que parecesse o mesmo para duas pesquisas: isto é, estabilizou. Curou muitos erros de badfile.
Steve Powell

1
Como um aparte, o Tomcat sofre esse problema ao soltar grandes arquivos WAR na pasta webapps de fontes remotas e tem feito isso por um longo tempo.
John Rix

Respostas:


21

No nível mais baixo, a única maneira de modelar esse utilitário é ter uma pesquisa de encadeamento em um diretório e observar os atributos do arquivo. Mas você pode usar padrões para desenvolver um adaptador para esse utilitário.

Por exemplo, servidores de aplicativos j2ee, como Tomcat e outros, têm um recurso de carregamento automático em que, assim que o descritor de implementação muda ou a classe de servlet muda, o aplicativo é reiniciado.

Você pode usar as bibliotecas de servidores como a maioria do código do tomcat é reutilizável e de código aberto.


59
Isso não é mais verdade no Java 7: agora há uma API para isso que pode se conectar aos serviços de notificação do sistema operacional: blogs.oracle.com/thejavatutorials/entry/…
Arnout Engelen

Essa API é altamente inadequada, ela não fornece notificação para eventos de fechamento de arquivo no Linux. Portanto, em um caso simples, quando um arquivo é fechado, copiá-lo para outro diretório não funciona.
binarytemple_picsolve

117

Já escrevi um monitor de arquivo de log antes e descobri que o impacto sobre o desempenho do sistema de pesquisar os atributos de um único arquivo, algumas vezes por segundo, é realmente muito pequeno.

Java 7, como parte do NIO.2, adicionou a API WatchService

A API WatchService foi projetada para aplicativos que precisam ser notificados sobre eventos de alteração de arquivo.


10
vejo os exemplos como diretório de observação, mas e um arquivo individual?
Archimedes Trajano

@ArchimedesTrajano A API avisa quando um arquivo em um diretório é alterado. O evento acionado inclui o nome do arquivo que foi alterado. Assim, você pode manipular eventos para um determinado arquivo ou arquivos e ignorar outros.
Michael



27

Existe uma biblioteca chamada jnotify que envolve o inotify no linux e também tem suporte para windows. Nunca usei e não sei o quão bom é, mas vale a pena tentar eu diria.


1
Funciona perfeitamente e é super simples de usar. Eu uso o inotify há muitos anos. É rápido, estável e confiável
oᴉɹǝɥɔ

8

Java commons-io tem um FileAlterationObserver . ele faz polling em combinação com um FileAlterationMonitor. Semelhante ao VFS comum. A vantagem é que ele tem muito menos dependências.

editar: Menos dependências não é verdade, são opcionais para VFS. Mas ele usa o arquivo java em vez da camada de abstração VFS.


4

"Mais recursos NIO" tem funcionalidade de observação de arquivo, com implementação dependente do sistema operacional subjacente. Deve estar em JDK7.


Você poderia ser mais específico ou postar um link para a documentação? Não consigo encontrar nada sobre isso ...
Luciano

Parece ser o pacote java.nio.file. Veja um tutorial .
David L.

4

Eu executo esse trecho de código toda vez que vou ler o arquivo de propriedades, apenas lendo o arquivo se ele foi modificado desde a última vez que o li. Espero que isso ajude alguém.

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

Abordagem semelhante é usada no Log4J FileWatchdog.


2

Você pode ouvir as alterações do arquivo usando um FileReader. Por favor, veja o exemplo abaixo

// 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 você está disposto a gastar algum dinheiro, JNIWrapper é uma biblioteca útil com um Winpack, você poderá obter eventos do sistema de arquivos em certos arquivos. Infelizmente, apenas o Windows.

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

Caso contrário, recorrer ao código nativo nem sempre é uma coisa ruim, especialmente quando o melhor em oferta é um mecanismo de votação em oposição a um evento nativo.

Percebi que as operações do sistema de arquivos Java podem ser lentas em alguns computadores e podem facilmente afetar o desempenho do aplicativo se não forem bem gerenciadas.


1

Você também pode considerar o Apache Commons JCI (Java Compiler Interface). Embora essa API pareça estar focada na compilação dinâmica de classes, ela também inclui classes em sua API que monitora as alterações de arquivo.

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




1

No entanto, pesquisar a última propriedade do arquivo modificado é uma solução simples, mas eficaz. Basta definir uma classe estendendo my FileChangedWatchere implementar o onModified()método:

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

Semelhante às outras respostas, aqui está como fiz isso usando File, Timer e TimerTask para permitir que isso seja executado como uma pesquisa de thread em segundo plano em intervalos definidos.

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

  }
}
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.