1、简介
java程序监控文件夹的变化可以使用“定时循环递归”的方式进行扫描,但是性能非常低。
除此之外,可以借助于操作系统的文件系统的事件驱动方式进行处理也就是文件系统有变化就会通知到java程序,既然需要操作系统的支持,就要有对应的dll文件才可以,目前已经有一款开源的框架jnotify,通过它就可以实现此功能。
2、代码
-
1、maven依赖
<!-- https://mvnrepository.com/artifact/jnotify/jnotify --> <dependency> <groupId>jnotify</groupId> <artifactId>jnotify</artifactId> <version>0.91</version> </dependency>
-
2、代码
import net.contentobjects.jnotify.JNotify; import net.contentobjects.jnotify.JNotifyException; import net.contentobjects.jnotify.JNotifyListener; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.PrintWriter; import java.util.concurrent.atomic.AtomicInteger; /** * 利用jnotify来实现监听文件夹及文件变化 * 这样就避免了不断递归扫描的问题 * * [@Author](https://my.oschina.net/arthor) liufu * [@E-mail](https://my.oschina.net/rosolio): liufuyanjin@1218.com.cn * @CreateTime 2018/9/17 16:24 */ public class JnotifyTest { public static void main(String[] args) throws InterruptedException, JNotifyException { // 打印lib路径, 看看在哪里设置jnotify比较合适 System.out.println(System.getProperty("java.library.path")); // 设置监听的文件夹,或者文件 String linstenerPath = "E:\\testPath"; // 指定监听的模式,创建、删除、修改、和重命名 int mask = JNotify.FILE_CREATED | JNotify.FILE_DELETED | JNotify.FILE_MODIFIED | JNotify.FILE_RENAMED; // 是否监控子目录 boolean watchSubtree = true; // 设置监听器,得到一个监听器id,可以通过这个id来移除监听器 int watchId = JNotify.addWatch(linstenerPath, mask, watchSubtree, new MyJnotifyListner()); // 如果需要去掉监听器,比如不需要监听某个目录了 //JNotify.removeWatch(watchId); //启动线程,不断的创建、重命名和删除文件,测试jnotify的响应速度 new DynamicBuildFile(linstenerPath).start(); // 一定要让程序等待,只要程序不退出,那么上面的监听器就一直有效 Thread.sleep(Integer.MAX_VALUE); } static class MyJnotifyListner implements JNotifyListener { AtomicInteger integer = new AtomicInteger(0); [@Override](https://my.oschina.net/u/1162528) public void fileCreated(int i, String rootPath, String name) { System.out.println("create a file : " + rootPath + "/" + name + ";integer:" + integer.incrementAndGet()); } @Override public void fileDeleted(int i, String rootPath, String name) { System.out.println("delete a file : " + rootPath + "/" + name + ";integer:" + integer.incrementAndGet()); } @Override public void fileModified(int i, String rootPath, String name) { System.out.println("modified a file : " + rootPath + "/" + name + ";integer:" + integer.incrementAndGet()); } @Override public void fileRenamed(int i, String rootPath, String oldName, String newName) { System.out.println("rename a file, oldName : " + (rootPath + "/" + oldName) + ";newName : " + (rootPath + "/" + newName) + "integer:" + integer.incrementAndGet()); } } /** * 动态生成和删除文件,测试jnotify的响应速度 * 总结 * 1、create 只会触发create * 2、delete 会触发 delete和modified * 3、rename 会出发一次rename 和modified * 4、如果编辑那个文件,那么会先产生一个临时文件(create),然后modified,在然后rename */ static class DynamicBuildFile extends Thread { String basePath; DynamicBuildFile(String basePath) { this.basePath = basePath; } @Override public void run() { while (true) { File file1 = new File(basePath + "\\aa1.txt"); File file2 = new File(basePath + "\\aa2.txt"); File file3 = new File(basePath + "\\aa3.txt"); try { PrintWriter printWriter1 = new PrintWriter(new FileOutputStream(file1)); //下面两行,都会触发modify printWriter1.println("dddd"); printWriter1.close(); PrintWriter printWriter2 = new PrintWriter(new FileOutputStream(file2)); printWriter2.println("dddd"); printWriter2.close(); PrintWriter printWriter3 = new PrintWriter(new FileOutputStream(file3)); printWriter3.println("dddd"); printWriter3.close(); file1.delete(); file2.delete(); file3.delete(); } catch (FileNotFoundException e) { e.printStackTrace(); } } } } }
3、问题
https://sourceforge.net/projects/jnotify/files/jnotify/
或者csdn
https://files.cnblogs.com/files/songxingzhu/jnotify-lib-0.94.zip
需要把对应的文件,放到操作系统对应的位置,可以在代码中打印
System.out.println(System.getProperty("java.library.path"));来查看可以放在什么地方下面任何一个地方都可以
D:\mysoft\JAVA\jdk1.8\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;D:\mysoft\Anaconda;D:\mysoft\Anaconda\Library\mingw-w64\bin;D:\mysoft\Anaconda\Library\usr\bin;D:\mysoft\Anaconda\Library\bin;D:\mysoft\Anaconda\Scripts;D:\mysoft\hadoop-2.6.4\bin;D:\mysoft\hadoop-2.6.4\sbin;D:\mysoft\Tomcat\apache-tomcat-8.5.11\bin;D:\mysoft\apache-maven-3.3.9\bin;D:\mysoft\JAVA\jdk1.8\bin;C:\ProgramData\Oracle\Java\javapath;D:\mysoft\Scala\bin;D:\mysoft\mysql\mysql-5.6.17-winx64\bin;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\ATI Technologies\ATI.ACE\Core-Static;D:\mysoft\SVN\bin;C:\Program Files\TortoiseSVN\bin;D:\mysoft\Scala\bin;D:\mysoft\Git\installLocation\cmd;C:\Program Files\nodejs\;C:\Program Files\OpenSSH\bin;C:\Users\Administrator\AppData\Roaming\npm;
经验:
在Windows上,复制jnotify_64bit.dll到:c:/windows下,修改名称为jnotify.dll即可。
在Linux上,可以复制libjnotify.so到/usr/lib64目录下。
4、总结
实现文件夹感知的方式有如下几种
- 1、自己写代码,递归遍历
- 2、使用common-io(内部实现递归遍历)
- 3、使用WatchService(jdk提供的)
- 4、jnotify(直接调用window、Linux的api,需要拷贝dll或者so文件到对应目录下,效率非常高)