Java Folder/File 观看

Java Folder/File watch

我正在尝试构建一个监视文件夹及其子文件夹以检测文件创建或修改的应用程序。要观看的文件总数将日益增加。

我曾尝试使用 java nio WatchService 和 apache common FileAlterationObserver。当文件 creation/modification 在 WatchKey 被获取之后和重置之前发生时,WatchService 有时会丢失事件。由于 FileAlterationObserver 基于轮询,当文件数量增加时性能也会下降。

构建此类应用程序的最佳方法是什么?

谢谢@DuncG。在完成上述示例后,我找到了解决问题的方法。

如果有人遇到同样的问题,请添加此示例代码。

在示例中,我将所有事件添加到一个集合中(这将删除重复的事件)并在 WatchKey 为空后处理保存的事件。处理保存的事件时,新目录将注册到 WatchService。

package com.filewatcher;

import java.io.File;
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.SimpleFileVisitor;
import java.nio.file.StandardWatchEventKinds;
import java.nio.file.WatchEvent;
import java.nio.file.WatchKey;
import java.nio.file.WatchService;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class WatchService implements Runnable {

private static final long POLL_DELAY = 3;

private static final Logger LOGGER = LoggerFactory.getLogger(WatchService.class);

private final WatchService watcher;

private final Map<WatchKey, Path> keys;

private final Set<Path> events = new HashSet<Path>();

public WatchService(Path dir) throws IOException {
    this.watcher = FileSystems.getDefault().newWatchService();
    this.keys = new HashMap<WatchKey, Path>();
    walkAndRegisterDirectories(dir);
}

@Override
public void run() {
    while (true) {
        try {
            WatchKey key;
            try {
                key = watcher.poll(POLL_DELAY, TimeUnit.SECONDS);
            } catch (InterruptedException x) {
                return;
            }

            if (key != null) {
                Path root = keys.get(key);
                for (WatchEvent<?> event : key.pollEvents()) {
                    Path eventPath = (Path) event.context();
                    if (eventPath == null) {
                        System.out.println(event.kind());
                        continue;
                    }

                    Path fullPath = root.resolve(eventPath);
                    events.add(fullPath);
                }

                boolean valid = key.reset();
                if (!valid) {
                    keys.remove(key);
                }
            } else {
                if (events.size() > 0) {
                    processEvents(events);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

/**
 * Process events and register new directory with watch service
 * @param events
 * @throws IOException
 */
private void processEvents(Set<Path> events) throws IOException {
    for (Path path : events) {
        // register directory with watch service if its not already registered
        if (Files.isDirectory(path, LinkOption.NOFOLLOW_LINKS) && !this.keys.containsValue(path)) {
            registerDirectory(path);
            // Since new directory was not registered, get all files inside the directory.
            // new/modified files after this will get notified by watch service
            File[] files = path.toFile().listFiles();
            for (File file : files) {
                LOGGER.info(file.getAbsolutePath());
            }
        } else {
            LOGGER.info(path.toString());
        }
    }
    // clear events once processed
    events.clear();
}

/**
 * Register a directory and its sub directories with watch service
 * @param root folder
 * @throws IOException
 */
private void walkAndRegisterDirectories(final Path root) throws IOException 
{
    Files.walkFileTree(root, new SimpleFileVisitor<Path>() {
        @Override
        public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
            registerDirectory(dir);
            return FileVisitResult.CONTINUE;
        }
    });
}

/**
 * Register a directory with watch service
 * @param directory
 * @throws IOException
 */
  private void registerDirectory(Path dir) throws IOException {
    WatchKey key = dir.register(this.watcher, StandardWatchEventKinds.ENTRY_CREATE,
            StandardWatchEventKinds.ENTRY_MODIFY);
    this.keys.put(key, dir);
  }
}

public class FileWatcherApplication implements CommandLineRunner {
    @Value("${filewatch.folder}")
    private String rootPath;

    public static void main(String[] args) {
        SpringApplication.run(FileWatcherApplication.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        File rootFolder = new File(rootPath);
        if (!rootFolder.exists()) {
            rootFolder.mkdirs();
        }

        new Thread(new WatchService(Paths.get(rootPath)), "WatchThread").start();
    }
}