WatchService递归监听目录的文件更新
优点
用WatchService对本地文件的监控,WatchService是基于本机操作系统实现对文件的监控,动态获取文件变化。添加自动回调函数,自定义处理方法
jdk版本
WatchService是jdk1.7版本引进的,位于nio包下
1 package aws; 2 3 4 import org.apache.log4j.Logger; 5 6 import java.io.File; 7 import java.io.FilenameFilter; 8 import java.nio.file.*; 9 import java.util.concurrent.Executors; 10 import java.util.regex.Pattern; 11 12 public class FileWatchUtil { 13 public static void main(String[] args) throws Exception { 14 15 FileWatchUtil.CallBack callBack = new FileWatchUtil.CallBack() { 16 @Override 17 public void CallBackMethod(String path) { 18 System.out.println("Hello Call Back ===>" + path); 19 } 20 }; 21 new FileWatchUtil("D:\\data\\", "test").setCallBack(callBack); 22 23 } 24 25 26 private String fileDirectory; 27 private String nameRule; 28 private CallBack callBack; 29 private static Logger logger = Logger.getLogger("CONSOLE"); 30 31 /** 32 * 构造函数 33 */ 34 public FileWatchUtil(String fileDirectory, String nameRule) { 35 this.fileDirectory = fileDirectory; 36 this.nameRule = nameRule; 37 } 38 39 /** 40 * 回调函数接口 41 * 42 * @author Administrator 43 */ 44 public static interface CallBack { 45 void CallBackMethod(String path); 46 } 47 48 /** 49 * 设置回调函数 50 */ 51 52 public void setCallBack(CallBack cb) { 53 callBack = cb; 54 if (callBack != null) { 55 logger.info("----callBack class==>" + callBack.toString()); 56 run(); 57 } 58 } 59 60 public void run() { 61 Path path = Paths.get(fileDirectory); 62 if (!Files.exists(path)) { 63 logger.info("----监听目录不存在!"); 64 System.exit(0); 65 } 66 // 根目录添加监听 67 watchDir(fileDirectory); 68 // 迭代扫描子目录并添加监听 69 findFilesPathAndWatch(fileDirectory); 70 } 71 72 private void findFilesPathAndWatch(String watchPath) { 73 try { 74 File rootFile = new File(watchPath); 75 if (rootFile.isDirectory()) { 76 String regex = nameRule; // 文件夹命名筛选规则 77 String path = watchPath; 78 FilenameFilter fileFilter = new FilenameFilter() { 79 public boolean accept(File dir, String name) { 80 // 文件名规则过滤 81 if (Pattern.matches(regex, name)) { 82 return false; 83 } 84 // 判断是否是文件夹 85 if (new File(path + File.separator + name).isDirectory()) { 86 return true; 87 } 88 return false; 89 } 90 }; 91 // ----文件筛选规则----- 92 File[] files = rootFile.listFiles(fileFilter); 93 for (File file : files) { 94 // 如果这个文件是目录,则进行递归搜索 95 if (file.isDirectory()) { 96 String newPath = file.getPath(); 97 // 迭代查找文件目录 98 findFilesPathAndWatch(newPath); 99 // logger.info("--find path:" + newPath); 100 // 添加文件监听 101 watchDir(newPath); 102 } 103 } 104 } 105 } catch (Exception e) { 106 e.printStackTrace(); 107 logger.error("----findFilesPathAndWatch error----: " + e.getMessage()); 108 } 109 } 110 111 private void watchDir(String watchDir) { 112 try { 113 //获取当前文件系统的监控对象 114 WatchService service = FileSystems.getDefault().newWatchService(); 115 // logger.info("----create watch service----"); 116 //获取文件目录下的Path对象注册到 watchService中。 监听的事件类型,有创建,删除,以及修改 117 Paths.get(watchDir).register(service, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY); 118 // logger.info("----register listening type----"); 119 // 新建线程添加监听 120 Executors.newSingleThreadExecutor().submit(WatchServiceRun(service, watchDir)); 121 } catch (Exception e) { 122 e.printStackTrace(); 123 logger.error("----watchDir error----: " + e.getMessage()); 124 } 125 } 126 127 private Runnable WatchServiceRun(WatchService service, String watchDir) { 128 Runnable runnable = new Runnable() { 129 @Override 130 public void run() { 131 Long id = Thread.currentThread().getId(); 132 logger.info(id + "--> watch path:" + watchDir); 133 while (true) { 134 try { 135 // 获取可用key.没有可用的就wait 136 WatchKey key = service.take(); 137 logger.info(id + "--> key" + key); 138 for (WatchEvent<?> event : key.pollEvents()) { 139 if (event.context() == null) { 140 continue; 141 } 142 143 // 判断监听的目录是否存在 144 if (Files.notExists(Paths.get(watchDir))) { 145 logger.info(id + "--> 监听目录名称已经修改:-->" + watchDir); 146 break; 147 } 148 149 String handleType = event.kind().toString(); 150 String path = watchDir + File.separator + event.context(); 151 // 判断子文件是否存在 152 if (Files.notExists(Paths.get(path))) { 153 logger.info(id + "--> 子文件已经删除(文件或目录): " + path + " 类型:" + handleType + " 次数: " + event.count()); 154 continue; 155 } 156 // 判断子文件类型是否是目录 157 if (Files.isDirectory(Paths.get(path))) { 158 // 只监听目录新增 159 if ("ENTRY_CREATE".equals(handleType)) { 160 logger.info(id + "--> 新曾子目录: " + path + " 类型:" + handleType + " 次数: " + event.count()); 161 watchDir(path); 162 } 163 } else { 164 logger.info(id + "--> 新增子文件: " + path + " 类型:" + handleType + " 次数: " + event.count()); 165 // 回调自定义处理函数 166 callBack.CallBackMethod(path); 167 } 168 } 169 // 重置,这一步很重要,否则当前的key就不再会获取将来发生的事件 170 boolean valid = key.reset(); 171 //失效状态,退出监听 172 if (!valid) { 173 // 监听目录删除后,退出目录监听 174 logger.info(id + "--> 退出目录监听:" + watchDir); 175 break; 176 } 177 // 判断目录是否存在,取消监听 178 if (Files.notExists(Paths.get(watchDir))) { 179 // 监听目录名称已经修改后取消原来的目录监听 180 logger.info(id + "--> 取消目录监听:" + watchDir); 181 break; 182 } 183 } catch (Exception e) { 184 e.printStackTrace(); 185 logger.error("----watchDir error----: " + e.getMessage()); 186 break; 187 } 188 } 189 } 190 }; 191 return runnable; 192 } 193 194 195 }
问题:
1. 自动循环添加监听 ==》 已解决
2. WatchService,重复触发Modify事件 (使用notepad ++编辑文件的话或触发两次 ENTRY_MODIFY监听,使用记事本则不会重复触发) ==》 未解决
3. 监听到文件变化时,自动回调 ==》已解决
作者:風巽千龍

浙公网安备 33010602011771号