在 Java 中观察目录的变化

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/23452527/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-13 23:04:10  来源:igfitidea点击:

Watching a Directory for Changes in Java

javafilejava.nio.file

提问by Isuru

I want to watch a directory for file changes. And I used WatchService in java.nio. I can successfully listen for file created event. But I can't listen for file modify event. I checked official java tutorial, but still struggling.

我想查看文件更改的目录。我在 java.nio 中使用了 WatchService。我可以成功侦听文件创建事件。但我无法监听文件修改事件。我查看了官方 Java 教程,但仍在苦苦挣扎。

Here is the source code.

这是源代码。

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

import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.WatchEvent;
import java.nio.file.WatchEvent.Kind;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;

public class MainWatch {

    public static void watchDirectoryPath(Path path) {
        // Sanity check - Check if path is a folder
        try {
            Boolean isFolder = (Boolean) Files.getAttribute(path,
                    "basic:isDirectory", NOFOLLOW_LINKS);
            if (!isFolder) {
                throw new IllegalArgumentException("Path: " + path
                        + " is not a folder");
            }
        } catch (IOException ioe) {
            // Folder does not exists
            ioe.printStackTrace();
        }

        System.out.println("Watching path: " + path);

        // We obtain the file system of the Path
        FileSystem fs = path.getFileSystem();

        // We create the new WatchService using the new try() block
        try (WatchService service = fs.newWatchService()) {

            // We register the path to the service
            // We watch for creation events
            path.register(service, ENTRY_CREATE);
            path.register(service, ENTRY_MODIFY);
            path.register(service, ENTRY_DELETE);

            // Start the infinite polling loop
            WatchKey key = null;
            while (true) {
                key = service.take();

                // Dequeueing events
                Kind<?> kind = null;
                for (WatchEvent<?> watchEvent : key.pollEvents()) {
                    // Get the type of the event
                    kind = watchEvent.kind();
                    if (OVERFLOW == kind) {
                        continue; // loop
                    } else if (ENTRY_CREATE == kind) {
                        // A new Path was created
                        Path newPath = ((WatchEvent<Path>) watchEvent)
                                .context();
                        // Output
                        System.out.println("New path created: " + newPath);
                    } else if (ENTRY_MODIFY == kind) {
                        // modified
                        Path newPath = ((WatchEvent<Path>) watchEvent)
                                .context();
                        // Output
                        System.out.println("New path modified: " + newPath);
                    }
                }

                if (!key.reset()) {
                    break; // loop
                }
            }

        } catch (IOException ioe) {
            ioe.printStackTrace();
        } catch (InterruptedException ie) {
            ie.printStackTrace();
        }

    }

    public static void main(String[] args) throws IOException,
            InterruptedException {
        // Folder we are going to watch
        // Path folder =
        // Paths.get(System.getProperty("C:\Users\Isuru\Downloads"));
        File dir = new File("C:\Users\Isuru\Downloads");
        watchDirectoryPath(dir.toPath());
    }
    }

回答by vvg

Actually you have incorrectly subscribed to events. Only last listener has been registered with ENTRY_DELETE events type.

实际上,您错误地订阅了事件。只有最后一个侦听器已使用 ENTRY_DELETE 事件类型注册。

To register for all kind of events at once you should use:

要一次注册所有类型的事件,您应该使用:

 path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE); 

回答by Hindol

Warning! Shameless self promotion!

警告!无耻的自我推销!

I have created a wrapper around Java 1.7's WatchServicethat allows registering a directory and any number of globpatterns. This class will take care of the filtering and only emit events you are interested in.

我已经创建了一个围绕 Java 1.7 的包装器WatchService,它允许注册一个目录和任意数量的glob模式。此类将负责过滤并仅发出您感兴趣的事件。

DirectoryWatchService watchService = new SimpleDirectoryWatchService(); // May throw
watchService.register( // May throw
        new DirectoryWatchService.OnFileChangeListener() {
            @Override
            public void onFileCreate(String filePath) {
                // File created
            }

            @Override
            public void onFileModify(String filePath) {
                // File modified
            }

            @Override
            public void onFileDelete(String filePath) {
                // File deleted
            }
        },
        <directory>, // Directory to watch
        <file-glob-pattern-1>, // E.g. "*.log"
        <file-glob-pattern-2>, // E.g. "input-?.txt"
        ... // As many patterns as you like
);

watchService.start();

Complete code is in this repo.

完整的代码在这个repo 中

回答by FrederikH

I made some classes for this.

我为此做了一些课程。

public interface FileAvailableListener {
    public void fileAvailable(File file) throws IOException;
}

and

public class FileChange {

private long lastModified;
private long size;
private long lastCheck;

public FileChange(File file) {
    this.lastModified=file.lastModified();
    this.size=file.length();
    this.lastCheck = System.currentTimeMillis();
}

public long getLastModified() {
    return lastModified;
}
public long getSize() {
    return size;
}
public long getLastCheck() {
    return lastCheck;
}

public boolean isStable(FileChange other,long stableTime) {
    boolean b1 = (getLastModified()==other.getLastModified());
    boolean b2 = (getSize()==other.getSize());
    boolean b3 = ((other.getLastCheck()-getLastCheck())>stableTime);
    return b1 && b2 && b3;
}
}

and

public class DirectoryWatcher {

private Timer timer;
private List<DirectoryMonitorTask> tasks = new ArrayList<DirectoryMonitorTask>();

public DirectoryWatcher() throws URISyntaxException, IOException, InterruptedException {
    super();
    timer = new Timer(true);        
}
public void addDirectoryMonitoringTask(DirectoryMonitorTask task,long period) {
    tasks.add(task);
    timer.scheduleAtFixedRate(task, 5000, period);      
}
public List<DirectoryMonitorTask> getTasks() {
    return Collections.unmodifiableList(tasks);
}
public Timer getTimer() {
    return timer;
}
}

and

class DirectoryMonitorTask extends TimerTask {

public final static String DIRECTORY_NAME_ARCHIVE="archive";
public final static String DIRECTORY_NAME_ERROR="error";
public final static String LOCK_FILE_EXTENSION=".lock";
public final static String ERROR_FILE_EXTENSION=".error";   
public final static String FILE_DATE_FORMAT="yyyyMMddHHmmssSSS";

private String name;
private FileAvailableListener listener;
private Path directory;
private File directoryArchive;
private File directoryError;
private long stableTime;
private FileFilter filter;
private WatchService watchService;
private SimpleDateFormat dateFormatter = new SimpleDateFormat(FILE_DATE_FORMAT);
private Hashtable<File,FileChange> fileMonitor = new Hashtable<File,FileChange>();

public DirectoryMonitorTask(String name,FileAvailableListener listener,Path directory,long stableTime,FileFilter filter) throws IOException {
    super();
    this.name=name;
    this.listener=listener;
    this.directory=directory;
    this.stableTime=stableTime;
    if (stableTime<1) {
        stableTime=1000;
    }
    this.filter=filter;
    validateNotNull("Name",name);
    validateNotNull("Listener",listener);
    validateNotNull("Directory",directory);
    validate(directory);
    directoryArchive = new File(directory.toFile(),DIRECTORY_NAME_ARCHIVE);
    directoryError = new File(directory.toFile(),DIRECTORY_NAME_ERROR);
    directoryArchive.mkdir();
    directoryError.mkdir();
    //
    log("Constructed for "+getDirectory().toFile().getAbsolutePath());

    initialize();
    //
    watchService = FileSystems.getDefault().newWatchService();
    directory.register(watchService,StandardWatchEventKinds.ENTRY_CREATE,StandardWatchEventKinds.ENTRY_DELETE,StandardWatchEventKinds.ENTRY_MODIFY);
    log("Started");
}

private void initialize() {
    File[] files = getDirectory().toFile().listFiles();
    for (File file : files) {
        if (isLockFile(file)) {
            file.delete();
        } else if (acceptFile(file)) {
            fileMonitor.put(file,new FileChange(file));
            log("Init file added -"+file.getName());
        }
    }
}
public SimpleDateFormat getDateFormatter() {
    return dateFormatter;
}
public Path getDirectory() {
    return directory;
}
public FileAvailableListener getListener() {
    return listener;
}
public String getName() {
    return name;
}
public WatchService getWatchService() {
    return watchService;
}
public long getStableTime() {
    return stableTime;
}
public File getDirectoryArchive() {
    return directoryArchive;
}
public File getDirectoryError() {
    return directoryError;
}
public FileFilter getFilter() {
    return filter;
}   
public Iterator<File> getMonitoredFiles() {
    return fileMonitor.keySet().iterator();
}

@Override
public void run() {
    WatchKey key;
    try {
        key = getWatchService().take();
        // Poll all the events queued for the key
        for (WatchEvent<?> event : key.pollEvents()) {                                      
            @SuppressWarnings("unchecked")
            Path filePath = ((WatchEvent<Path>) event).context();
            File file = filePath.toFile();
            if ((!isLockFile(file)) && (acceptFile(file))) {
                switch (event.kind().name()) {
                    case "ENTRY_CREATE":
                        //                          
                        fileMonitor.put(file,new FileChange(file));
                        log("File created ["+file.getName()+"]");
                        break;
                        //
                    case "ENTRY_MODIFY":
                        //                          
                        fileMonitor.put(file,new FileChange(file));
                        log("File modified ["+file.getName()+"]");
                        break;  
                        //
                    case "ENTRY_DELETE":
                        //
                        log("File deleted ["+file.getName()+"]");
                        createLockFile(file).delete();
                        fileMonitor.remove(file);                           
                        break;
                        //
                }
            }
        }
        // reset is invoked to put the key back to ready state
        key.reset();
    } catch (InterruptedException e) {              
        e.printStackTrace();
    }

    Iterator<File> it = fileMonitor.keySet().iterator();

    while (it.hasNext()) {
        File file = it.next();  
        FileChange fileChange = fileMonitor.get(file);
        FileChange fileChangeCurrent = new FileChange(file);

        if (fileChange.isStable(fileChangeCurrent, getStableTime())) {
            log("File is stable ["+file.getName()+"]");
            String filename = getDateFormatter().format(new Date())+"_"+file.getName();
            File lockFile = createLockFile(file);
            if (!lockFile.exists()) {
                log("File do not has lock file ["+file.getName()+"]");
                try {
                    Files.createFile(lockFile.toPath());
                    log("Processing file ["+file.getName()+"]");
                    getListener().fileAvailable(file);                      
                    file.renameTo(new File(getDirectoryArchive(),filename));
                    log("Moved to archive file ["+file.getName()+"]");
                } catch (IOException e) {                       
                    file.renameTo(new File(getDirectoryError(),filename));
                    createErrorFile(file,e);
                    log("Moved to error file ["+file.getName()+"]");
                } finally {
                    lockFile.delete();

                }
            } else {                    
                log("File do has lock file ["+file.getName()+"]");
                fileMonitor.remove(file);
            }               
        } else {                
            log("File is unstable ["+file.getName()+"]");
            fileMonitor.put(file,fileChangeCurrent);
        }
    }       
}

public boolean acceptFile(File file) {
    if (getFilter()!=null) {
        return getFilter().accept(file);
    } else {
        return true;
    }       
}

public boolean isLockFile(File file) {
    int pos = file.getName().lastIndexOf('.');
    String extension="";
    if (pos!=-1) {
        extension = file.getName().substring(pos).trim().toLowerCase();
    }   
    return(extension.equalsIgnoreCase(LOCK_FILE_EXTENSION));
}

private File createLockFile(File file) {
    return new File(file.getParentFile(),file.getName()+LOCK_FILE_EXTENSION);
}

private void createErrorFile(File file,IOException exception) {
    File errorFile = new File(file.getParentFile(),file.getName()+ERROR_FILE_EXTENSION);

    StringWriter sw = null;
    PrintWriter pw = null;
    FileWriter fileWriter = null;
    try {
        //          
        fileWriter = new FileWriter(errorFile);
        if (exception!=null) {
            sw = new StringWriter();
            pw = new PrintWriter(sw);
            exception.printStackTrace(pw);      
            fileWriter.write(sw.toString());
        } else {
            fileWriter.write("Exception is null.");
        }
        //      
        fileWriter.flush();
        //
    } catch (IOException e) {
    } finally {
        if (sw!=null) {
            try {
                sw.close();
            } catch (IOException e1) {              
            }
        }
        if (pw!=null) {
            pw.close();
        }
        if (fileWriter!=null) {
            try {
                fileWriter.close();
            } catch (IOException e) {                   
            }
        }
    }
}

private void validateNotNull(String name,Object obj) {
    if (obj==null) {
        throw new NullPointerException(name+" is null.");
    }           
}       
private void validate(Path directory) throws IOException {          
    File file = directory.toFile();
    if (!file.exists()) {
        throw new IOException("Directory ["+file.getAbsolutePath()+"] do not exists.");
    } else if (!file.isDirectory()) {
        throw new IOException("Directory ["+file.getAbsolutePath()+"] is not a directory.");
    } else if (!file.canRead()) {               
        throw new IOException("Can not read from directory ["+file.getAbsolutePath()+"].");
    } else if (!file.canWrite()) {
        throw new IOException("Can not write to directory ["+file.getAbsolutePath()+"] .");
    }       
}

private void log(String msg) {
    //TODO
    System.out.println("Task ["+getName()+"] "+msg);
}
}

回答by P RAJESH

package p1;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.util.List;

public class WatchForFile {

    public void WatchMyFolder(String path )
    {
        File dir = new File(path);
        Path myDir= dir.toPath();
          try 
          {
              Boolean isFolder = (Boolean) Files.getAttribute(myDir,"basic:isDirectory", NOFOLLOW_LINKS);
              if (!isFolder)
              {
                  throw new IllegalArgumentException("Path: " + myDir + " is not a folder");
              }
          }
          catch (IOException ioe)
          {
              ioe.printStackTrace();
          }

          System.out.println("Watching path: " + myDir);

        try {
           WatchService watcher = myDir.getFileSystem().newWatchService();
           myDir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE,StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY);

           WatchKey watckKey = watcher.take();

           List<WatchEvent<?>> events = watckKey.pollEvents();

           for (WatchEvent event : events) {
                if (event.kind() == StandardWatchEventKinds.ENTRY_CREATE) {
                    System.out.println("Created: " + event.kind().toString());

                }
                if (event.kind() == StandardWatchEventKinds.ENTRY_DELETE) {
                    System.out.println("Delete: " + event.context().toString());
                }
                if (event.kind() == StandardWatchEventKinds.ENTRY_MODIFY) {
                    System.out.println("Modify: " + event.context().toString());
                }
            }

        }
        catch (Exception e) 
        {
            System.out.println("Error: " + e.toString());
        }
    }
}

回答by omkar marathe

Check this Code...

检查此代码...

https://github.com/omkar9999/FileWatcherHandler

https://github.com/omkar9999/FileWatcherHandler

This project allows watching files for different file events like create, modify & delete and then act on these events in a generic way.

该项目允许针对不同的文件事件(如创建、修改和删除)监视文件,然后以通用方式对这些事件进行操作。

How to Use?
Create a Path object representing the directory to monitor for file events.

如何使用?
创建一个 Path 对象,表示要监视文件事件的目录。

Path path = Paths.get("/home/omkar/test");

Implement the FileHandler interface to perform an action detected by file event registered.

实现 FileHandler 接口以执行由注册的文件事件检测到的操作。

public class FileHandlerTest implements FileHandler {

    private static final Logger LOGGER = Logger.getLogger(FileHandlerTest.class.getName());

    /*
     * This implemented method will delete the file
     * 
     * @see com.io.util.FileHandler#handle(java.io.File,
     * java.nio.file.WatchEvent.Kind)
     */
    public void handle(File file, Kind<?> fileEvent) {
        LOGGER.log(Level.INFO,"Handler is triggered for file {0}",file.getPath());
        if(fileEvent == StandardWatchEventKinds.ENTRY_CREATE) {
            try {
                boolean deleted = Files.deleteIfExists(Paths.get(file.getPath()));
                assertTrue(deleted);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

}

Create an instance of an Implemented FileHandler

创建已实现的 FileHandler 的实例

FileHandlerTest fileHandlerTest = new FileHandlerTest();

Create an instance of a FileWatcher by passing path, an instance of an Implemented FileHandler, and types of file events that you want to monitor separated by commas.

通过传递路径、已实现的 FileHandler 的实例以及要监视的文件事件类型(以逗号分隔)来创建 FileWatcher 的实例。

FileWatcher fileWatcher = new FileWatcher(path, fileHandlerTest, StandardWatchEventKinds.ENTRY_CREATE);

Now Create and start a new Thread.

现在创建并启动一个新线程。

Thread watcherThread = new Thread(fileWatcher);
watcherThread.start();

This thread will start polling for your registered file events and will invoke your custom handle method once any of the registered events are detected.

该线程将开始轮询您注册的文件事件,并在检测到任何注册事件后调用您的自定义句柄方法。

回答by user6440666

public class FileWatcher implements Runnable {

private static final Logger LOGGER =Logger.getLogger(FileWatcher.class.getName());

private WatchService watcher;
private FileHandler fileHandler;
private List<Kind<?>> watchedEvents;
private Path directoryWatched;

/**
 * @param directory
 * @Path directory to watch files into
 * @param fileHandler
 * @FileHandler implemented instance to handle the file event
 * @param watchRecursive
 *            if directory is to be watched recursively
 * @param watchedEvents
 *            Set of file events watched
 * 
 * @throws IOException
 */
public FileWatcher(Path directory, FileHandler fileHandler, boolean watchRecursive,
        WatchEvent.Kind<?>... watchedEvents) throws IOException {
    super();
    this.watcher = FileSystems.getDefault().newWatchService();
    this.fileHandler = fileHandler;
    this.directoryWatched = directory;
    this.watchedEvents = Arrays.asList(watchedEvents);
    if (watchRecursive) {
        // register all subfolders
        Files.walkFileTree(directory, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
                LOGGER.log(Level.INFO, "Registering {0} ", dir);
                dir.register(watcher, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE,
                        StandardWatchEventKinds.ENTRY_MODIFY);
                return FileVisitResult.CONTINUE;
            }
        });
    } else {
        directory.register(watcher, watchedEvents);
    }
}

@SuppressWarnings({ "unchecked" })
public void run() {
    LOGGER.log(Level.INFO, "Starting FileWatcher for {0}", directoryWatched.toAbsolutePath());
    WatchKey key = null;
    while (true) {
        try {
            key = watcher.take();
            if (key != null) {
                for (WatchEvent<?> event : key.pollEvents()) {
                    WatchEvent.Kind<?> kind = event.kind();

                    WatchEvent<Path> ev = (WatchEvent<Path>) event;
                    //directory in which file event is detected
                    Path directory = (Path) key.watchable(); 
                    Path fileName = ev.context();
                    if (watchedEvents.contains(kind)) {
                        LOGGER.log(Level.INFO, "Invoking handle on {0}", fileName.toAbsolutePath());
                        fileHandler.handle(directory.resolve(fileName).toFile(), kind);
                    }
                }
                key.reset();
            }
        } catch (InterruptedException ex) {
            LOGGER.log(Level.SEVERE, "Polling Thread was interrupted ", ex);
            Thread.currentThread().interrupt();
        }
    }
}

}

}