我有一个简单的Java实用程序类,它可以观察我注册的目录。我的目标:每当文件在该目录中发生变化时,我想捕获该事件并处理该文件......
我只为ENTRY_MODIFY事件注册了一个目录 - 这个全部在我的本地Windows 7计算机上运行,运行时为1.8.0_510-b16。
我启动程序,启动监控过程。然后我更改了该目录中的文件,它识别出更改,但由于某种原因,它产生的不是一个而是两(2)个通知事件 - 对于同一个文件,两者都是相同的ENTRY_MODIFY类型,为什么会这样做?
为方便起见,这是我的完整代码:
package com.xxx.yyy.zzz.lcm;
//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 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 java.util.Arrays;
/**
* This program uses Java 7 java.nio.* package to Watch for CHANGEs on a set of files that you specify at
* startup using command line arguments
*
* Whenever a registered file is changed, the code will Copy that file to a target directory
* and give it a new name using the following pattern:
* Copy sourcefilepath/eventfilename to targetfilepath/yyyymmdd:HHmmssSSS_eventfilename
*/
public class FileWatcher {
public static void main(String[] args) {
// Make sure argument list is not empty
if (args.length == 0){
System.out.println("You must specify proper arguments for this utility to run");
useage();
}
/*
* Prolog - get the arguments specified
* First argument is the targetDir
* Arguments 2nd, 3rd, etc fieles to be monitored
*/
String targetDirArg = args[0];
String[] filesForMonitoring = new String[args.length - 1]; //
// Strip off the first entry from the arguments (that's target)
for(int i=1; i < args.length; i++) {
filesForMonitoring[i-1] = args[i];
}
// Simple Edits:
if (targetDirArg == null) {
System.out.println("Target directory you specified ('" + targetDirArg + "') + was not valid.");
useage();
}
if (filesForMonitoring.length < 1) {
System.out.println("You must specify at least one (or more) files to monitor using command line arguments\n");
useage();
}
// Printout the runtime specs
System.out.println("*** Runtime Parms ***");
System.out.println("Target directory:");
System.out.println("\t" + targetDirArg);
System.out.println("Filepaths to be watched:");
for (int i=0; i < filesForMonitoring.length; i++) {
System.out.println("\t" + filesForMonitoring[i]);
}
// Now create the WatcherServices and watch the specified assets
/*
* TO-DO Test it thoroughly and figure out why it sometimes produces 2 or 3 events for a single file change
* I have seen this numerous times but have not been able to reproduce consistently, but I've observed that
* when monitoring a single file, if I make a change to that file, sometimes that change will produce 2 or
* even 3 separate events, which is unusual and we need to find out why it's happening.
*/
try {
// Create the WatchService object
WatchService watcher = FileSystems.getDefault().newWatchService();
// Register the directory path that you want to monitor
// TO-DO: Modify this logic to register multiple files (if more than one specified) for monitoring
Path sourceDir = Paths.get(filesForMonitoring[0]);
sourceDir.register(watcher, ENTRY_MODIFY);
System.out.println("\nWatchService watching for file changes in dir: " + sourceDir.getParent() + "\\" + sourceDir.getFileName());
/*
* This infinite loop that monitors the specified directory
* Each directory that is registered will produce a watch key if changed
*/
while (true) {
WatchKey key;
try {
key = watcher.take();
} catch (InterruptedException ex) {
return;
}
/*
* For each directory modified, find out the Events
* (there may be multiple events) and the file name,
* then process accordingly
* TO DO: Find out WHY there are sometimes multiple events
* for a single filename.... this is problematic
*/
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
// Get the Event kind and the filename for that Event
@SuppressWarnings("unchecked")
WatchEvent<Path> ev = (WatchEvent<Path>) event;
Path eventFileName = ev.context();
System.out.println("\n" + kind.name() + ": " + eventFileName);
// This is how we can check for a change to the specific file
if (kind == ENTRY_MODIFY &&
eventFileName.toString().endsWith(".properties")) {
System.out.println("Content of property file '" + eventFileName.toString() + "' has changed!!!");
try {
processFile(Paths.get("C:sourcefile"), Paths.get("C:targetfile"));
} catch (IOException iox) {
iox.printStackTrace();
}
}
}
/*
* The reset() method is very important to ensure flow of events
* The only time it may be invalid is if the monitored directory got deleted
*/
boolean valid = key.reset();
if (!valid) {
break;
}
}
} catch (IOException ex) {
System.err.println(ex);
}
}
private static void processFile(Path source, Path target) throws IOException {
System.out.println("Processing file change. Source='" + source.getFileName() + "', Target=" + target.getFileName() + "'");
}
public static void useage() {
System.out.println("Useage: prompt>java FileWatcher c:/targetdir c:/sourcepath1/sourcefile1.txt c:/sourcepath1/sourcefile2.txt");
System.out.println("\t The above command would monitor sourcefile1.txt and sourcefile2.txt for any changes");
System.out.println("\t If any change to one of those files, the code would copy the conents of the changed file(s) from");
System.out.println("\t sourcepath1 to the target path specified in args[0]. The target file would also be given a new name with timestamp");
System.exit(1);
}
}