有三种方式:
1、java common.io 内部实现是遍历的方式,小文件夹的效率还好,比如我测试60G的目录,就很慢很慢了。
2、jdk 7 的watch service //没有测试
3、jnotify 直接调用windows的api,效率很高,也很简单,推荐使用。

------------------------------------------------------------------------------------------------------------------------------------------------------------
common.io
需要java.io 2.1及其以上版本。版本地址如下:
http://commons.apache.org/io/download_io.cgi

import org.apache.commons.io.monitor.FileAlterationListenerAdaptor; 


/** 

 * 自定义文件监听器 

 * @author 

 * @date 2010-11-16 

 * @file org.demo.file.MyFileListener.java 

 */ 

public class MyFileListener extends FileAlterationListenerAdaptor{ 

 @Override 

 public void onFileCreate(File file) { 

 System.out.println("[新建]:" + file.getAbsolutePath()); 

 } 

 @Override 

 public void onFileChange(File file) { 

 System.out.println("[修改]:" + file.getAbsolutePath()); 

 } 

 @Override 

 public void onFileDelete(File file) { 

 System.out.println("[删除]:" + file.getAbsolutePath()); 

 } 

} 



import org.apache.commons.io.monitor.FileAlterationMonitor; 

import org.apache.commons.io.monitor.FileAlterationObserver; 



public class Test { 


 /** 

 * @param args 

 */ 

 public static void main(String[] args) throws Exception{ 

 // 监控目录 

 String rootDir = "d:\\Temp"; 

 // 轮询间隔 5 毫秒 

 long interval = 5l; 

 // 

 FileAlterationObserver observer = new FileAlterationObserver( 

 rootDir,null, 

 null); 

 observer.addListener(new MyFileListener()); 

 FileAlterationMonitor monitor = new FileAlterationMonitor(interval,observer); 

 // 开始监控 

 monitor.start(); 

 } 

} 

------------------------------------------------------------------------------------------------------------------------------------------------------------


jdk7 watchservice
详见
Java SE 7 Tutorial中增加了一个监控目录变更情况的示例,用于介绍其新发布的WatchService API。



但对于用惯了.NET FileWatcher的用户而言,如果用于项目我认为它有两个欠缺:

1、应该提供一个独立线程后台运行机制,让这个监控过程自己在后台转,不影响前端处理

2、 Java不像.NET有内置的源生事件机制,不过可以借助它内置的Observer/Observable对象用观察者模式实现准事件



下面是把Java SE Tutorial示例中无关内容删除,补充上述两个扩展后的实现,因为这个API比较新,也希望能和大家多多探讨:



1、参考.NET定义事件参数对象

package marvellousworks.practicalpattern.concept.unittest; 


import java.nio.file.WatchEvent.Kind; 


/** 

 * 文件系统事件类型 

 * @author wangxiang 

 * 

 */ 

public final class FileSystemEventArgs { 

 private final String fileName; 

 private final Kind<?> kind; 


 public FileSystemEventArgs(String fileName, Kind<?> kind){ 

 this.fileName = fileName; 

 this.kind = kind; 

 } 


 /** 

 * 文件的路径 

 */ 

 public String getFileName(){return fileName;} 


 /** 

 * 操作类型:变更、创建、删除 

 */ 

 @SuppressWarnings("rawtypes") 

 public Kind getKind(){return kind;} 

}




2、定义DirectoryWatcher,用于监控某个文件夹,至于如何扩展FileWatcher则可以在这个基础上通过限定文件名称和操作类型的方式扩展



package marvellousworks.practicalpattern.concept.unittest; 


import java.io.IOException; 

import java.nio.file.FileSystems; 

import java.nio.file.Path; 

import java.nio.file.Paths; 

import java.nio.file.WatchEvent; 

import java.nio.file.WatchEvent.Kind; 

import java.nio.file.WatchKey; 

import java.nio.file.WatchService; 

import java.util.Observable; 

import java.util.concurrent.Callable; 

import java.util.concurrent.Executor; 

import java.util.concurrent.Executors; 

import java.util.concurrent.FutureTask; 


import static java.nio.file.StandardWatchEventKinds.*; 


/** 

 * 监控一个目录内文件的更新、创建和删除事件(不包括子目录) 

 * 

 * 对于http://download.oracle.com/javase/tutorial/essential/io/notification.html进行了改造 

 * 使其更接近.NET的DirectoryWatcher使用习惯 

 * 

 * 由于java没有类似.NET源生的事件机制 

 * 因此实现上采用了Java SE自带的Observer/Observable对象对外抛出“假”事件 

 * 

 * 适于Java SE 7 

 * 

 * @author wangxiang 

 * 

 */ 

public class DirectoryWatcher extends Observable{ 


 private WatchService watcher; 

 private Path path; 

 private WatchKey key; 

 private Executor executor = Executors.newSingleThreadExecutor(); 


 FutureTask<Integer> task = new FutureTask<Integer>( 

 new Callable<Integer>(){ 

 public Integer call() throws InterruptedException{ 

 processEvents(); 

 return Integer.valueOf(0);}}); 


 @SuppressWarnings("unchecked") 

 static <T> WatchEvent<T> cast(WatchEvent<?> event) { 

 return (WatchEvent<T>) event; 

 } 


 public DirectoryWatcher(String dir) throws IOException { 

 watcher = FileSystems.getDefault().newWatchService(); 

 path = Paths.get(dir); 

 // 监控目录内文件的更新、创建和删除事件 

 key = path.register(watcher, ENTRY_MODIFY, ENTRY_CREATE, ENTRY_DELETE); 

 } 


 /** 

 * 启动监控过程 

 */ 

 public void execute(){ 

 // 通过线程池启动一个额外的线程加载Watching过程 

 executor.execute(task); 

 } 


 /** 

 * 关闭后的对象无法重新启动 

 * @throws IOException 

 */ 

 public void shutdown() throws IOException { 

 watcher.close(); 

 executor = null; 

 } 


 /** 

 * 监控文件系统事件 

 */ 

 void processEvents() { 

 while (true) { 

 // 等待直到获得事件信号 

 WatchKey signal; 

 try { 

 signal = watcher.take(); 

 } catch (InterruptedException x) { 

 return; 

 } 


 for (WatchEvent<?> event : signal.pollEvents()) { 

 Kind<?> kind = event.kind(); 


 // TBD - provide example of how OVERFLOW event is handled 

 if (kind == OVERFLOW) { 

 continue; 

 } 


 // Context for directory entry event is the file name of entry 

 WatchEvent<Path> ev = cast(event); 

 Path name = ev.context(); 

 notifiy(name.getFileName().toString(), kind); 

 } 

 // 为监控下一个通知做准备 

 key.reset(); 

 } 

 } 


 /** 

 * 通知外部各个Observer目录有新的事件更新 

 */ 

 void notifiy(String fileName, Kind<?> kind){ 

 // 标注目录已经被做了更改 

 setChanged(); 

 // 主动通知各个观察者目标对象状态的变更 

 // 这里采用的是观察者模式的“推”方式 

 notifyObservers(new FileSystemEventArgs(fileName, kind)); 

 } 

}




3、单元测试

package marvellousworks.practicalpattern.concept.unittest; 


import static org.junit.Assert.*; 


import java.io.File; 

import java.io.IOException; 

import java.util.ArrayList; 

import java.util.List; 

import java.util.Observable; 

import java.util.Observer; 


import org.junit.Test; 

import static java.nio.file.StandardWatchEventKinds.*; 


public class DirectoryWatcherFixture { 


 private static final String DIR_PATH =System.getProperty("user.dir"); 

 private static final File DIR = new File(DIR_PATH); 

 private static final String SUFFIX = ".txt"; 

 private static final String PREFIX = "test"; 

 private static final int ADD_TIMES = 3; 


 /** 

 * 观察者 

 * @author wangxiang 

 * 

 */ 

 public class Logger implements Observer{ 

 @Override 

 public void update(Observable observable, Object eventArgs) { 

 FileSystemEventArgs args = (FileSystemEventArgs) eventArgs; 

 System.out.printf("%s has been %s\n", args.getFileName(), args.getKind()); 

 assertTrue(args.getFileName().startsWith(PREFIX)); 

 assertEquals(ENTRY_CREATE, args.getKind()); 

 } 

 } 


 @Test 

 public void testWatchFile() throws IOException, InterruptedException{ 

 DirectoryWatcher watcher = new DirectoryWatcher(DIR_PATH); 

 Logger l1 = new Logger(); 

 watcher.addObserver(l1); 

 watcher.execute(); 


 // 创建一系列临时文件 

 List<String> files = new ArrayList<>(); 

 for(int i=0; i<ADD_TIMES; i++){ 

 files.add(File.createTempFile(PREFIX, SUFFIX, DIR).toString()); 

 } 


 // 延迟等待后台任务的执行 

 Thread.sleep(4000); 

 watcher.shutdown(); 

 System.out.println("finished"); 

 } 

} 



 Console窗口显示的测试内容 




test5769907807190550725.txt has been ENTRY_CREATE 

test4657672246246330348.txt has been ENTRY_CREATE 

test1823102943601166149.txt has been ENTRY_CREATE 

finished 


------------------------------------------------------------------------------------------------------------------------------------------------------------ 

jnotify 

详见 http://www.blogjava.net/pengo/archive/2011/01/09/342622.html 

jnotify分为64位 和 32位。 详见 http://jnotify.sourceforge.net/ 

public class Test { 


 /** 

 * @param args 

 */ 

 public static void main(String[] args) throws Exception{ 

 // 监控目录 

 String rootDir = "Z:\\"; 

 String path = rootDir; 

 int mask = JNotify.FILE_CREATED | JNotify.FILE_DELETED 

 | JNotify.FILE_MODIFIED | JNotify.FILE_RENAMED; 

 boolean watchSubtree = true; 

 while (true) { //否则一次就完了 

 System.out.println("begin watch"); 

 JNotify.addWatch(path, mask, watchSubtree, new MyJnotifyListner()); 

 Thread.sleep(10000); 

 System.out.println("end watch"); 

 } 

 } 


} 



import net.contentobjects.jnotify.JNotifyListener; 



public class MyJnotifyListner implements JNotifyListener { 

 public void fileRenamed(int wd, String rootPath, String oldName, 

 String newName) { 

 System.out.println("文件:" + rootPath + " : " + oldName + " 重命名为: " 

 + newName + "\n"); 

 } 


 public void fileModified(int wd, String rootPath, String name) { 

 System.out.println("文件修改 " + rootPath + " : " + name + "\n"); 

 } 


 public void fileDeleted(int wd, String rootPath, String name) { 

 System.out.println("删除文件: " + rootPath + " : " + name + "\n"); 

 } 


 public void fileCreated(int wd, String rootPath, String name) { 

 System.out.println("新建文件: " + rootPath + " : " + name + "\n"); 

 } 

}