`

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统计文件夹大小

    在Java编程中,统计文件夹大小是一项常见的任务,特别是在处理大量数据或者文件系统操作时。在JDK 7及以上版本中,Java引入了新的`java.nio.file`包,提供了更高效且灵活的方式来处理文件和目录。这个功能实现利用了...

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

    总结来说,这个项目通过ZJPFileListener和ZJPFileMonitor两个类,实现了基于Java的文件监控功能,能够实时监测文件夹中文件的状态变化,并按照设定的时间间隔进行检查。这对于需要实时跟踪文件系统变化的系统来说...

    Java实现实时监控目录下文件变化的方法

    Java实现实时监控目录下文件变化的方法 Java实现实时监控目录下文件变化的方法是指通过Java语言来实现实时监控目录下文件的变化情况,包括文件的创建、删除、修改等操作。这种方法可以应用于各种场景,例如文件系统...

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

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

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

    本项目“Java监控目录文件夹程序”是利用Java编写的一个实用工具,旨在实时监控指定目录下的文件和文件夹变化。这个程序每5秒进行一次自动扫描,更新并显示目录中的文件大小和数量变化,对于开发者来说,这种功能...

    java监视文件夹

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

    Java获取文件夹大小、文件大小,文件个数

    在Java编程中,获取文件或文件夹的大小以及文件个数是常见的操作,尤其是在处理文件上传、存储管理和性能监控等场景。以下将详细介绍如何通过Java实现这些功能。 首先,我们来探讨如何获取单个文件的大小。在Java中...

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

    Java作为多平台支持的编程语言,提供了丰富的库和工具来实现FTP功能,包括下载、上传文件以及定时监控等操作。本篇文章将深入探讨如何使用Java进行FTP文件传输,并涉及自动解压和压缩的功能。 首先,让我们关注Java...

    文件夹监控

    文件夹监控是一种重要的系统管理工具,它允许用户或者系统管理员实时跟踪和记录指定目录下的文件和子文件夹的变动情况。这种技术在多种场景下都十分有用,例如数据备份、安全审计、软件开发过程中的版本控制,以及...

    目录监控 监控文件夹

    1. 使用编程语言:例如Python的watchdog库,Java的WatchService API,JavaScript的chokidar库等,可以自定义监控规则并处理事件。 2. 命令行工具:在Linux上,使用inotify命令行工具,配合shell脚本实现自动化监控。...

    java合并两个文件夹内容

    Java脚本由于其跨平台性和丰富的库支持,常被用于编写运维工具,实现文件管理、系统监控、数据处理等功能。 综上所述,Java提供了一套强大的文件操作API,能够方便地实现合并文件夹这样的任务。在实际应用中,我们...

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

    在Java编程环境中,实现Windows文件系统的操作监控是一个有趣且实用的技术挑战。这涉及到对系统底层事件的捕获,通常需要借助于系统钩子(System Hooks)的概念。在Java中,虽然原生支持并不完善,但我们可以通过...

    java实现文件监控小工具

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

    java实现遍历文件夹内pdf分别转成一张jpg图片

    2. **Java文件I/O操作**:为了遍历文件夹并处理每个PDF文件,Java的`java.io`包提供了必要的API,如`File`类用于表示文件和目录,`FileInputStream`和`FileOutputStream`用于读写文件,以及`FileVisitor`接口可以...

    java项目 监控

    【标题】:“java项目 监控” 在Java编程领域,"监控"通常指的是应用程序性能监控(APM)或系统监控。这个小型Java项目显然旨在提供一个解决方案,以便开发者或者运维人员能够实时跟踪和分析Java应用的运行状态。...

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

    在Java中实现实时监听文件夹是否有新文件增加并上传服务器的功能主要涉及到使用Java NIO包中的WatchService接口,以及利用Timer定时任务执行的机制。本文将通过实例代码展示如何结合这两种技术来达到上述功能,并将...

    java 监听本地文件自动上传服务器

    在IT行业中,Java作为一种强大的编程语言,被广泛用于各种任务,包括文件系统监控和网络通信。本篇文章将详细探讨如何使用Java实现本地文件监听,并自动将其上传至服务器,以实现文件同步功能。 首先,让我们从...

    TXT_test.rar_test.txt文_监控文件夹_监控文件夹下的文件生成

    首先,我们需要理解如何在编程环境中实现文件夹监控。在许多编程语言中,如Python、Java或C#,都有库或者内置功能可以用来监视文件系统的变更。例如,Python中的`watchdog`库允许我们监听文件或目录的变化。以下是一...

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

    在IT行业中,实时监控目录(文件夹)的更新状态并实现与外部目录的同步操作是一项常见且重要的任务,尤其在自动化、数据备份、文件同步等场景下。这个例子中的小工具利用了.NET Framework中的`FileSystemWatcher`类...

    java使用WatchService监控文件夹示例

    在本文中,我们将深入探讨如何使用Java的`WatchService` API来监控文件夹。 `WatchService` API是自Java 7开始引入的,位于`java.nio.file`包下。以下是一些关键的概念和方法: 1. **WatchService**:这是核心接口...

Global site tag (gtag.js) - Google Analytics