通过 Java 去监测某个目录下的文件变动
2017-03-18 15:29
309 查看
最近处理了一个需求,大概是这样的:
己方搭建好FTP服务器
对方往该服务器的指定目录(假设叫 目录A)上传文件
己方需要将对方上传好的文件(处于上传中状态的文件不能进行处理)解析并更新到数据库中
己方对 目录A 只有 “读”的权限,即,不能对 目录A中的文件进行删除、重命名、移动等操作。
对于这个需求,我一开始想出的 解决方案 是:
开启一个线程,定期去读取 目录A 下的所有文件
将每两次读取的文件列表进行对比,新出现的文件名对应的文件就是对方新上传的
对于新的文件,先记录下它的 大小 和 最后修改时间,然后,隔2秒,再次去读它的这两个属性值。如果这两个值保持不变了,那么就说明文件上发传好了。如果发生了改变,那 么,就再隔2秒再去确定一次。
确定文件上传好了之后,解析文件并上更新到数据库中
这个方案在一般情况下是可以胜任的,但是它隐藏以下两个小问题:
读取目录A的间隔的不太好设定,设定得小的话,会使得读取的频率太频繁;设定得大的话,又可能导致文件大量积压
获取 大小 和 最后修改时间 这两个属性的值的时间间隔也不好确定,上面说的是 2秒,是我自己的假想。因为当遇到大文件时,极有可能在2秒之内是不会传完的。如果FTP是搭建在 windows 操作系统上的话,会有下面这个问题:
一个文件在传输之初时,就已经将文件大小确定了,在传输过程中,通过 java 中 File 类的 lengh()去查看的话,它的值是不会发生变化的。
对于 最后修改时间这个属性,只有在文件创建之初和文件传输完比之后,才会发变改变,在传输过程中,通过 java 的 File 类的 lastModifiedTime() 去查看的话,它的值也是不会发变化的
如果FTP是搭建在 Unix 操作系统上的话,是没有上面这个问题,在整个文件传输过程中, 大小 和 最后修改时间 这两个属性是一直在变化的。(我在 CentOS7 上验证过)
既然上面这个方案有缺陷,那就想想其他方案吧。
后来,在同事的点拨下,找到了 JDK7 中增加的新的 API:File Watch Service。
这个API的思路,其实跟 观察者 模式是一样的:对指定的目录注册一个 Watcher,当目录下的文件发生变化时,Java通知你这个 Watcher 说文件变化了。这样一来,你就可以进行处理了。
下面直接上代码:
对于上面代码中 “说明点1” ,补充以下几点说明:
这种通过判断 文件大小 和 文件最后修改时间 处理方式只限于 Unix 操作系统,原因在上面已经说过了。
对于 windows 系统,应该在产生 ENTRY_CREATE 这个事件后,继续监听,直到产了一个该文件的“ENTRY_MODIFY”事件,或者 ENTRY_DELETE 事件,才说明该文件是传输完毕或者被取消传输了。
内嵌的 Thread 最好另建一个 类,这样看起来会比较容易理解。
参考文档
Oracle 官方示例
己方搭建好FTP服务器
对方往该服务器的指定目录(假设叫 目录A)上传文件
己方需要将对方上传好的文件(处于上传中状态的文件不能进行处理)解析并更新到数据库中
己方对 目录A 只有 “读”的权限,即,不能对 目录A中的文件进行删除、重命名、移动等操作。
对于这个需求,我一开始想出的 解决方案 是:
开启一个线程,定期去读取 目录A 下的所有文件
将每两次读取的文件列表进行对比,新出现的文件名对应的文件就是对方新上传的
对于新的文件,先记录下它的 大小 和 最后修改时间,然后,隔2秒,再次去读它的这两个属性值。如果这两个值保持不变了,那么就说明文件上发传好了。如果发生了改变,那 么,就再隔2秒再去确定一次。
确定文件上传好了之后,解析文件并上更新到数据库中
这个方案在一般情况下是可以胜任的,但是它隐藏以下两个小问题:
读取目录A的间隔的不太好设定,设定得小的话,会使得读取的频率太频繁;设定得大的话,又可能导致文件大量积压
获取 大小 和 最后修改时间 这两个属性的值的时间间隔也不好确定,上面说的是 2秒,是我自己的假想。因为当遇到大文件时,极有可能在2秒之内是不会传完的。如果FTP是搭建在 windows 操作系统上的话,会有下面这个问题:
一个文件在传输之初时,就已经将文件大小确定了,在传输过程中,通过 java 中 File 类的 lengh()去查看的话,它的值是不会发生变化的。
对于 最后修改时间这个属性,只有在文件创建之初和文件传输完比之后,才会发变改变,在传输过程中,通过 java 的 File 类的 lastModifiedTime() 去查看的话,它的值也是不会发变化的
如果FTP是搭建在 Unix 操作系统上的话,是没有上面这个问题,在整个文件传输过程中, 大小 和 最后修改时间 这两个属性是一直在变化的。(我在 CentOS7 上验证过)
既然上面这个方案有缺陷,那就想想其他方案吧。
后来,在同事的点拨下,找到了 JDK7 中增加的新的 API:File Watch Service。
这个API的思路,其实跟 观察者 模式是一样的:对指定的目录注册一个 Watcher,当目录下的文件发生变化时,Java通知你这个 Watcher 说文件变化了。这样一来,你就可以进行处理了。
下面直接上代码:
import java.io.File; 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.WatchKey; import java.nio.file.WatchService; import static java.nio.file.StandardWatchEventKinds.*; public class Sample { private WatchService watcher; private Path path; public Sample(Path path) throws IOException { this.path = path; watcher = FileSystems.getDefault().newWatchService(); this.path.register(watcher, OVERFLOW, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY); } public void handleEvents() throws InterruptedException { // start to process the data files while (true) { // start to handle the file change event final WatchKey key = watcher.take(); for (WatchEvent<?> event : key.pollEvents()) { // get event type final WatchEvent.Kind<?> kind = event.kind(); // get file name @SuppressWarnings("unchecked") final WatchEvent<Path> pathWatchEvent = (WatchEvent<Path>) event; final Path fileName = pathWatchEvent.context(); if (kind == ENTRY_CREATE) { // 说明点1 // create a new thread to monitor the new file new Thread(new Runnable() { public void run() { File file = new File(path.toFile().getAbsolutePath() + "/" + fileName); boolean exist; long size = 0; long lastModified = 0; int sameCount = 0; while (exist = file.exists()) { // if the 'size' and 'lastModified' attribute keep same for 3 times, // then we think the file was transferred successfully if (size == file.length() && lastModified == file.lastModified()) { if (++sameCount >= 3) { break; } } else { size = file.length(); lastModified = file.lastModified(); } try { Thread.sleep(500); } catch (InterruptedException e) { return; } } // if the new file was cancelled or deleted if (!exist) { return; } else { // update database ... } } }).start(); } else if (kind == ENTRY_DELETE) { // todo } else if (kind == ENTRY_MODIFY) { // todo } else if (kind == OVERFLOW) { // todo } } // IMPORTANT: the key must be reset after processed if (!key.reset()) { return; } } } public static void main(String args[]) throws IOException, InterruptedException { new Sample(Paths.get(args[0])).handleEvents(); } }
对于上面代码中 “说明点1” ,补充以下几点说明:
这种通过判断 文件大小 和 文件最后修改时间 处理方式只限于 Unix 操作系统,原因在上面已经说过了。
对于 windows 系统,应该在产生 ENTRY_CREATE 这个事件后,继续监听,直到产了一个该文件的“ENTRY_MODIFY”事件,或者 ENTRY_DELETE 事件,才说明该文件是传输完毕或者被取消传输了。
内嵌的 Thread 最好另建一个 类,这样看起来会比较容易理解。
参考文档
Oracle 官方示例
相关文章推荐
- 通过 Java 去监测某个目录下的文件变动(转)
- java File类的使用 通过递归打印文件目录
- java通过ftp上传、下载文件,遍历文件目录
- 通过java递归思想实现以树形方式展现出该目录中的所有子目录和文件
- java File 通过递归方式删除文件和目录
- java通过ftp上传、下载文件,遍历文件目录
- 通过java递归思想实现以树形方式展现出该目录中的所有子目录和文件
- java通过ftp上传、下载文件,遍历文件目录
- Java 通过调用系统程序打开文件或者目录的方式
- 通过Java代码打开浏览器,本地文件目录以及ftp站点
- 通过Eclipse创建一个Project ,Java Project 和Tomcat Project 生成的目录和文件
- 用JAVA遍历目录下的所有文件
- 用java写了一个实用的类----递归的拷贝给定目录的.java文件到指定的一个文件中
- 一个使用多线程删除指定目录及子目录下所有指定文件的Java程序(源码)
- JAVA 如何创建/删除/修改/复制目录及文件
- Java的目录文件读取
- 通过encryption.exe对java的class文件进行加密
- 通过Java直接对Excel文件进行操作
- SQL里,怎样通过递归得到某个文件目录下的全部文件名[含子目录]?
- 通过预先设定的系统时间来自动取走自动生成的文件到指定的目录下!!!