`

java7文件夹监控

    博客分类:
  • Java
 
阅读更多
java7的文件夹监控真是太烂了,折腾了一天,封装的差不多了,结果还是没有jnotify好用!!! (java7在递归子文件夹目录时貌似原理上有问题,应该真的就是每个文件夹单独注册监听事件吧,所以在拷贝整个文件夹到所监听的目录时,由于还没注册好监听事件,有些文件的创建事件根本无法监听到)!!!

虽然还得加额外的dll,还是用jnotify吧~~,
jnotify网址:http://jnotify.sourceforge.net/

当然,java7可以很好地控制监控的文件夹(不一定整个目录树都得监控不是,自己筛选挺方便)
java7的文件夹监控官方教程:http://docs.oracle.com/javase/tutorial/essential/io/notification.html


以下程序改自官方教程,主要是对事件进行了整合(原本更改文件名称被分为了两个事件delete+create,诸如此类):

import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import static java.nio.file.StandardWatchEventKinds.ENTRY_CREATE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_DELETE;
import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY;
import static java.nio.file.StandardWatchEventKinds.OVERFLOW;

import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.HashMap;

/*
 * java7文件夹监控程序的封装。
 * version: 2012_04_03
 */
public final class DirWatcher {
	public static interface WatchEventHandler {
		void handleEvent(KeyState event);
	}

	/**
	 * 记录操作类型
	 * 
	 */
	static enum OperationType {
		/**
		 * @author LC 调整
		 */
		Modify {
			@Override
			public String toString() {
				return "Modify";
			}
		},
		/**
		 * @author LC 新建
		 */
		Create {
			@Override
			public String toString() {
				return "Create";
			}
		},
		/**
		 * @author LC 删除
		 */
		Delete {
			@Override
			public String toString() {
				return "Delete";
			}
		},
		/**
		 * @author LC 空事件
		 */
		Null {
			@Override
			public String toString() {
				return "Null";
			}
		},
		/**
		 * @author LC 重命名
		 */
		Rename {
			@Override
			public String toString() {
				return "Rename";
			}
		},
		/**
		 * @author LC 没用到
		 */
		Move {
			@Override
			public String toString() {
				return "Move";
			}
		}
	}

	/**
	 * 记录{@link WatchKey}及事件的状态类
	 */
	class KeyState {
		/**
		 * 事件发生的路径或者{@link WatchKey}对应的路径
		 */
		public Path path;
		/**
		 * {@link WatchKey}上一步操作的路径或者重命名事件中的重命名后的名称
		 */
		public Path exPath;
		/**
		 * 事件的类型
		 */
		public OperationType opType = OperationType.Null;
		/**
		 * {@link WatchKey}的等级或者引发事件的{@link WatchKey}的等级
		 */
		public int level;
		/**
		 * 事件发生的事件
		 */
		public long opTime = -1;

		/**
		 * @param path
		 *            事件发生的路径或者{@link WatchKey}对应的路径
		 * @param level
		 *            {@link WatchKey}的等级或者引发事件的{@link WatchKey}的等级
		 */
		public KeyState(Path path, int level) {
			this.path = path;
			this.level = level;
		}

		/**
		 * @param path
		 *            事件发生的路径或者{@link WatchKey}对应的路径
		 * @param opType
		 *            操作的类型
		 * @param level
		 *            {@link WatchKey}的等级或者引发事件的{@link WatchKey}的等级
		 */
		public KeyState(Path path, OperationType opType, int level) {
			this.path = path;
			this.level = level;
			this.opType = opType;
		}

		/**
		 * @param path
		 *            事件发生的路径或者{@link WatchKey}对应的路径
		 * @param opType
		 *            操作的类型
		 * @param level
		 *            {@link WatchKey}的等级或者引发事件的{@link WatchKey}的等级
		 * @param opTime
		 *            事件发生的时间
		 */
		public KeyState(Path path, OperationType opType, int level, long opTime) {
			super();
			this.path = path;
			this.opType = opType;
			this.level = level;
			this.opTime = opTime;
		}

		@Override
		public String toString() {
			StringBuilder sb = new StringBuilder();
			sb.append("opType=" + opType);
			sb.append("\topTime=" + opTime);
			sb.append("\tlevel=" + level);
			if (path != null)
				sb.append("\tpath=" + path.normalize());
			if (exPath != null)
				sb.append("\texPath=" + exPath.normalize());

			return sb.toString();
		}

	}

	private Path pathToMonitor;

	private WatchService ws;
	/**
	 * 是否监控子目录树
	 */
	private boolean watchSubDir = false;
	/**
	 * 存储键值对应的路径等信息
	 */
	private HashMap<WatchKey, KeyState> keyMap = new HashMap<>();
	/**
	 * 监控的最大层级
	 */
	private int maxLevel = 100;
	/**
	 * 是否输出调试信息
	 */
	static boolean debug = true;
	/**
	 * 处理事件的函数类
	 */
	private WatchEventHandler eventHandler;

	private static final WatchEventHandler nullHandler = new WatchEventHandler() {

		@Override
		public void handleEvent(KeyState event) {
			if (!debug) {
				System.out.println(event);
				System.out.println();
			}
		}
	};

	/**
	 * 演示用
	 * 
	 * @param pathToMonitor
	 * @param watchSubDir
	 */
	public DirWatcher(Path pathToMonitor, boolean watchSubDir) {
		this.pathToMonitor = pathToMonitor;
		this.watchSubDir = watchSubDir;
		try {
			ws = FileSystems.getDefault().newWatchService();
			if (watchSubDir)
				registerAll(pathToMonitor, maxLevel);
			else
				register(pathToMonitor, 0);
		} catch (IOException e) {
			e.printStackTrace();
		}

		eventHandler = nullHandler;

	}

	public DirWatcher(Path pathToMonitor, boolean watchSubDir, int maxLevel,
			WatchEventHandler eventHandler) {
		super();
		this.pathToMonitor = pathToMonitor;
		this.watchSubDir = watchSubDir;
		this.maxLevel = maxLevel;
		this.eventHandler = eventHandler == null ? eventHandler : nullHandler;
	}

	@SuppressWarnings("unchecked")
	static <T> WatchEvent<T> cast(WatchEvent<?> event) {
		return (WatchEvent<T>) event;
	}

	/**
	 * 注册{@link WatchService}
	 * 
	 * @param pathToMonitor
	 *            需要被监控的目录
	 * @param level
	 *            相对于最上层的要监控的目录的等级,如d:\test为第0级,则d:\test\folder则为1级
	 * @throws IOException
	 */
	private void register(Path pathToMonitor, int level) throws IOException {
		WatchKey key = pathToMonitor.register(ws, ENTRY_CREATE, ENTRY_DELETE,
				ENTRY_MODIFY);
		//		System.out.println(key + "\t注册key," + dir.toFile().getAbsolutePath());
		if (debug) {
			KeyState prev = keyMap.get(key);
			if (prev == null) {
				System.out.format("-->\tregister: %s\tregisted level=%d\n",
						pathToMonitor, level);
			} else {
				if (!pathToMonitor.equals(prev.path)) {
					System.out.format(
							"-->\tupdate: %s -> %s\tregisted level=%d\n", prev,
							pathToMonitor, level);
				}
			}
		}
		KeyState prev = keyMap.get(key);
		if (prev == null)
			keyMap.put(key, new KeyState(pathToMonitor, level));
		else {
			prev.path = pathToMonitor;
			prev.level = level;
		}
	}

	/**
	 * 监控一个目录及其子目录
	 * 
	 * @param curPath
	 *            要被监控的目录
	 * @param maxLevel
	 *            监控的等级数量
	 * @throws IOException
	 */
	private void registerAll(final Path curPath, final int maxLevel)
			throws IOException {
		registerAll(curPath, 0, maxLevel);
	}

	/**
	 * 监控一个目录及其子目录
	 * 
	 * @param curPath
	 *            要被监控的目录
	 * @param curLevel
	 *            当前目录相对于最上层的要监控的目录的等级,如d:\test为第0级,则d:\test\folder则为1级
	 * @param maxLevel
	 *            监控的等级数量,curLevel<maxLevel时,才进行监控
	 * @throws IOException
	 */
	private void registerAll(final Path curPath, final int curLevel,
			final int maxLevel) throws IOException {

		Files.walkFileTree(curPath, new SimpleFileVisitor<Path>() {
			int curLv = curLevel;

			@Override
			public FileVisitResult preVisitDirectory(Path dir,
					BasicFileAttributes attrs) throws IOException {
				if (curLv < maxLevel)
					register(dir, curLv);
				++curLv;
				if (curLv < maxLevel) {
					return FileVisitResult.CONTINUE;
				} else {
					return FileVisitResult.SKIP_SUBTREE;
				}
			}

			@Override
			public FileVisitResult postVisitDirectory(Path dir, IOException exc)
					throws IOException {
				--curLv;
				return super.postVisitDirectory(dir, exc);
			}
		});
	}

	/**
	 * 开始监控,事件的处理分为两类
	 * <p>
	 * 1、对重命名或新增的文件夹的监控,由Oracle的示例完成。
	 * <p>
	 * 2、文件夹、文件的删除、重命名、新建事件;对于这类事件,Java7处理的不好,会有重复的,对此进行了封装。原理是根据事件发生的顺序及时间间隔来判断
	 * ,如同一个key的删除接着一个新建实际上为重命名事件
	 * <p>
	 * 存在的问题:由于Java7系统函数的问题,当把一个含有文件的目录拷贝到监控中的目录时,有些文件无法扫描到!!!!(子目录的监控没有问题)
	 * 
	 */
	public void startMonitorDir() {
		new Thread(new Runnable() {
			@Override
			public void run() {
				while (true) {
					// 等待监控的事件的发生
					WatchKey key;
					try {
						key = ws.take();
					} catch (InterruptedException e) {
						e.printStackTrace();
						return;
					}

					//事件的有效性检测
					KeyState stateOrigin = keyMap.get(key);
					Path dir = stateOrigin.path;
					if (dir == null) {
						System.err.println("WatchKey not recognized!!");
					}

					//
					int keyEventCount = 0;
					ArrayList<KeyState> eventList = new ArrayList<>();
					for (WatchEvent<?> event : key.pollEvents()) {
						Kind<?> kind = event.kind();
						// 遇到了不关心的事件
						if (kind == OVERFLOW) {
							continue;
						}
						++keyEventCount;

						// 获取文件路径
						WatchEvent<Path> ev = cast(event);
						Path name = ev.context();//相对路径
						Path child = dir.resolve(name);//绝对路径

						//增加到事件队列中
						KeyState eventState = new KeyState(child,
								stateOrigin.level);//不使用level字段
						eventState.opTime = System.currentTimeMillis();
						if (kind == ENTRY_CREATE) {
							eventState.opType = OperationType.Create;
						} else if (kind == ENTRY_DELETE) {
							eventState.opType = OperationType.Delete;
						} else if (kind == ENTRY_MODIFY)
							eventState.opType = OperationType.Modify;
						eventList.add(eventState);

						//事件的信息
						if (debug)
							System.out.format("%d:%s: %s\n", keyEventCount,
									event.kind().name(), child);

						// 如果有新的子目录被创建(或者其他目录更名为该目录),则加入新的子目录到监控列表中
						if (watchSubDir && (kind == ENTRY_CREATE)) {
							try {
								if (Files.isDirectory(child, NOFOLLOW_LINKS)) {
									if (debug)
										System.out.println(key + "\t原始key,"
												+ keyMap.get(key));
									registerAll(child, stateOrigin.level + 1,
											maxLevel);
								}
							} catch (IOException e) {
								//e.printStackTrace();
							}
						}
					}

					//分析事件队列,获取正确的事件
					if (eventList.size() == 1) {
						KeyState state = eventList.get(0);
						processEvent(key, state);
					} else if (eventList.size() > 1) {

						KeyState eventStart;

						final int size = eventList.size();
						for (int i = 0; i < size; i++) {
							eventStart = eventList.get(i);
							if (i + 1 < size) {
								KeyState eventEnd = eventList.get(i + 1);
								//重命名,先删除再创建
								if (eventStart.opType == OperationType.Delete
										&& eventEnd.opType == OperationType.Create) {
									eventStart.opType = OperationType.Rename;
									eventStart.exPath = eventEnd.path;
									processEvent(key, eventStart);
									++i;
									for (int j = i + 1; j < size; j++) {//过滤掉紧跟的modify事件
										KeyState tmp = eventList.get(j);
										if (tmp.path.equals(eventStart.exPath))
											i++;
									}
								} else {
									processEvent(key, eventStart);
									for (int j = i + 1; j < size; j++) {//过滤掉紧跟的modify事件
										KeyState tmp = eventList.get(j);
										if (tmp.path.equals(eventStart.path))
											i++;
									}

								}

							} else
								processEvent(key, eventStart);

							//							if(i+1>=size||evtList.get(i+1).lastOpType!=)

						}

					}
					if (debug)
						System.out.println();
					// reset key and remove from set if directory no longer accessible
					boolean valid = key.reset();
					if (!valid) {
						keyMap.remove(key);
						// all directories are inaccessible
						if (keyMap.isEmpty()) {
							break;
						}
					}

				}
			}

		}).start();
	}

	/**
	 * 处理事件,仍需要进行过滤
	 * 
	 * @param key
	 * @param event
	 */
	private void processEvent(WatchKey key, KeyState event) {
		KeyState keyState = keyMap.get(key);
		if (event.opType != OperationType.Modify
				|| !event.path.equals(keyState.exPath)
				|| keyState.opTime - event.opTime > 200) {//同一文件的两个modify时间过短,过滤掉一个(之前也有过滤,但中间可能插入了文件夹的modify事件);注意keyState中exPath是事件记录,path是监控的文件夹路径

			if (event.opType != OperationType.Modify
					|| !Files
							.isDirectory(event.path, LinkOption.NOFOLLOW_LINKS)) {//过滤文件夹的modify事件,毕竟文件夹modify是因为文件夹下的文件的改变,已经有对应事件了!!!当然,理论上应该再检测一下文件夹是否处于监控列表中,懒得写了,反正都打算用jnotify了!!!
				if (debug)
					System.out.println("key=" + key.hashCode() + "\t" + event);
				eventHandler.handleEvent(event);
			}
		}
		keyState.exPath = event.opType == OperationType.Rename ? event.exPath
				: event.path;//作为上一步操作的文件进行记录
		keyState.opTime = event.opTime;
	}

	public static void main(String[] args) {
		DirWatcher watcher = new DirWatcher(Paths.get("D:\\test2"), true);
		watcher.startMonitorDir();
	}
}

分享到:
评论

相关推荐

    java统计文件夹大小

    基于jdk7或以上java版本的文件夹大小统计功能实现,相对于传统的java获取文件再计算大小的方式要快速很多,支持统计指定类型,是否统计子文件夹

    java编写的ftp文件实时监控下载上传

    用java语言编写的ftp小工具,可以按指定时间监控ftp服务器,把服务器指定目录内新产生的文件或者文件夹下载到本地指定文件夹,下载后删除数据。 也可以监控本地文件夹,把文件夹内新产生的文件或者文件夹整体上传到...

    src.rar_java监控文件夹_文件监控_监控文件

    监控某一文件夹下文件状态包括增加、修改、删除,每隔5s检查一次

    java监视文件夹

    项目里用到的临时写的代码,反复对某文件夹里的文件做出同一动作使用

    Java监控目录文件夹程序.rar

    Java监控目录文件夹程序,用Java写的简单目录监视系统,每5秒会自动扫描一次被监视的文件夹,可让用户了解该目录中文件大小及文件增减数目的变化。

    java写的ftp下载上传定时监控

    java写的ftp,下载上传文件,定时监控下载上传,自动解压加压,

    java实现文件监控小工具

    java 代码仅供参考学习! java实现文件监控小工具 ...用于代码审计时监控文件夹内文件的变动,支持Mac,Windows,Linux。 第一次运行时请根据提示在此页面下载对应动态依赖库放到指定位置即可(注意版本!)。

    java实现windows文件系统操作监控

    java实现的一个监控windows文件夹中的文件的增删改等操作,根据不同的需要修改代码,可以做成不同的功能,如文件检索,文件保护,文件自动加密等程序应用

    java使用WatchService监控文件夹示例

    本篇文章主要介绍了java使用WatchService监控文件夹示例的资料,这里整理了详细的代码,有需要的小伙伴可以参考下。

    Java 实现实时监听文件夹是否有新文件增加并上传服务器功能

    本文中主要陈述一种实时监听文件夹中是否有文件增加的功能,可用于实际文件上传功能的开发。本文通过实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友参考下吧

    java键盘鼠标模拟监控

    系统文件夹: 获取系统特殊文件加的路径和图标。 系统快捷方式: 管理系统快捷方式。 系统相关信息: 收集系统CPU,内存,环境变量等相关信息。 系统会话: 提供关机,休眠,重启,注销等功能。 系统IO及网络功能: ...

    JNotify监控文件夹及文件变化.zip

    通过java代码利用JNotify工具实现监控一个文件夹下的文件或者文件夹的动态变化,资料包含文件代码,maven依赖,dll动态库(win和linux)

    实时监控目录(文件夹)的更新状态,与外部目录同步操作。(自己做的一个例子)

    懒得写了,总是上传失败。 公司业务写的一个小工具。

    java 读取远程共享文件

    java 读取远程共享文件,一个操作简单的读取远程资源的控件。

    基于java的远程监控系统的设计与实现【文献综述】.pdf

    文献综述 计算机科学与技术 基于 java 的远程监控系统的设计与实现 一、前言 近年来,随着计算机及网络的应用普及,千千万万的人们在娱乐、通讯、学习、工作等 各方面都实现了前所未有的信息化,极大地提高了生活...

    java写的远程监控程序

    用java写的远程监控软件,包括远程cmd命令,屏幕监控,摄像头操作,键盘钩子,远程注册表操作,以及文件的上传下载. 其中使用了一下第三方的开源jar包和dll文件,不是跨平台的,一些功能只能在windows上使用. 基本...

    java远程监控代码及可执行jar文件

    用java写的远程监控软件,包括远程cmd命令,屏幕监控,摄像头操作,键盘钩子,远程注册表操作,以及文件的上传下载. 其中使用了一下第三方的开源jar包和dll文件,不是跨平台的,一些功能只能在windows上使用. 基本...

    基于JAVA CS远程监控系统软件的实现(源代码+论文)

    远程监控软件一般分两个部分:一部分是客户端程序Client,另一部分是服务器端程序Server,由于本毕业设计的通信模块有别于传统的远程监控软件,...基于Java的后端远程系统软件的实现源码实现,适合计算机专业的毕业设计

    课程设计 基于JAVA CS远程监控系统软件的实现(源代码+论文).rar

    期末大作业,毕业设计 课程设计基于JAVA CS远程监控系统软件的实现(源代码+论文)。每到期末和毕业季,很多大四同学苦于没有参考的毕设资料,或者下载的资料不全、代码有问题,数据有问题等等,造成毕设出现问题影响...

    spring boot文件夹文件监听程序

    基于springboot的文件夹监听和遗漏文件自动获取功能以及springboot编译的直接可运行的bat文件

Global site tag (gtag.js) - Google Analytics