Java中的文件更改了监听器


104

我想在文件系统中的文件更改时收到通知。除了线程轮询lastModified File属性外,我什么都没有找到,显然,该解决方案不是最佳解决方案。


感谢您的所有回复。由于我的需求很小(我只需要在Web应用程序中的更改中重新加载属性文件即可。.主要是因为重新启动整个Webapp有点慢),所以我将坚持使用轮询模型。至少现在我知道对于这些情况没有简单的解决方案。
cheng81 2009年

14
请注意。当我们解决此问题之前,我们发现大文件倾向于缓慢进入,并且轮询机制经常在完全写入之前发现了新文件或更改过的文件。为了解决这个问题,采用了“两口”解决方案。轮询者注意到文件已更改,但是直到两次轮询看起来相同时才通知系统新文件/已更改文件:即已稳定。治愈了很多badfile错误。
史蒂夫·鲍威尔

1
顺便说一句,当从远程源中将大的WAR文件拖放到webapps文件夹中时,Tomcat遭受了这个问题,并且这样做已经很长时间了。
约翰·里克斯

Answers:


21

在较低的级别上,对该实用程序进行建模的唯一方法是在目录上进行线程轮询,并监视文件的属性。但是您可以使用模式来开发此类实用程序的适配器。

例如,诸如Tomcat之类的j2ee应用服务器和其他服务器具有自动加载功能,一旦部署描述符更改或servlet类更改,应用程序就会重新启动。

您可以使用此类服务​​器中的库,因为tomcat的大多数代码都是可重用的并且是开源的。


59
这不再是在Java 7中真:现在有这个,可以挂接到操作系统的通知服务的API:blogs.oracle.com/thejavatutorials/entry/...
Arnout恩格伦

该API严重不足,它不提供Linux上文件关闭事件的通知。因此,在一个简单的情况下,当文件关闭时,将其复制到另一个目录不起作用。
binarytemple_picsolve

117

我以前写过一个日志文件监视器,发现每秒轮询几次单个文件的属性对系统性能的影响实际上很小。

Java 7作为NIO.2的一部分,添加了WatchService API

WatchService API专为需要通知文件更改事件的应用程序而设计。


10
我将示例视为监视目录,但是单个文件呢?
阿基米德·特拉哈诺

@ArchimedesTrajano当目录中的文件已更改时,API会通知您。触发的事件包括已更改文件的名称。因此,您可以处理某个文件或多个文件的事件,而忽略其他文件。
迈克尔



27

有一个名为jnotify的库,它在Linux 上包装了inotify,也支持Windows。从未使用过它,我不知道它有多好,但是我想尝试一下是值得的。


1
它工作完美,使用起来超级简单。我使用inotify已经很多年了。它快速,稳定且可靠
2013年

8

Java commons-io具有FileAlterationObserver。它结合FileAlterationMonitor进行轮询。与Commons VFS类似。优点是它具有更少的依赖性。

编辑:较少的依赖项不是正确的,它们对于VFS是可选的。但是它使用Java File而不是VFS抽象层。


4

“更多NIO功能”具有文件监视功能,其实现取决于底层操作系统。应该在JDK7中。


您能具体一点还是发布文档链接?似乎无法在此上找到任何东西……
Luciano

好像是包java.nio.file。参见教程
David L.

4

每次我去读取属性文件时,我都会运行此代码段,仅在自上次读取以来已对其进行了修改的情况下才实际读取该文件。希望这对某人有帮助。

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

Log4J中使用了类似的方法FileWatchdog


2

您可以使用FileReader监听文件更改。请看下面的例子

// 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

如果您愿意花一些钱,JNIWrapper是Winpack的有用库,您将能够获取某些文件的文件系统事件。不幸的是只有窗户。

参见https://www.teamdev.com/jniwrapper

否则,诉诸本机代码并不总是一件坏事,尤其是当提供的最佳方法是针对本机事件的轮询机制时。

我注意到Java文件系统的操作在某些计算机上可能很慢,如果处理不当,很容易影响应用程序的性能。





1

但是,轮询最后修改的文件属性是一个简单而有效的解决方案。只需定义一个扩展my的类FileChangedWatcher并实现该onModified()方法即可:

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

与其他答案类似,这是我使用File,Timer和TimerTask使其以设置的间隔作为后台线程轮询运行的方式。

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

  }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.