Просмотр каталога изменений в Java

Я хочу посмотреть каталог изменений файла. И я использовал WatchService в java.nio. Я могу успешно прослушать созданное файл. Но я не могу слушать событие изменения файла. Я проверил официальный java-учебник, но все еще борется.

Вот исходный код.

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());
    }
    }

Ответ 1

На самом деле вы неправильно подписались на события. Только последний слушатель был зарегистрирован с типом событий ENTRY_DELETE.

Чтобы зарегистрироваться на все виды событий одновременно, вы должны использовать:

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

Ответ 2

Предупреждение! Бесстыдная самореклама!

Я создал оболочку для Java 1.7 WatchService которая позволяет регистрировать каталог и любое количество шаблонов глобуса. Этот класс будет заботиться о фильтрации и генерировать только те события, которые вас интересуют.

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();

Полный код в этом репо.

Ответ 3

Я сделал несколько классов для этого.

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

и

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;
}
}

и

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;
}
}

и

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);
}
}

Ответ 4

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());
        }
    }
}

Ответ 5

Проверьте этот код...

https://github.com/omkar9999/FileWatcherHandler

Этот проект позволяет просматривать файлы для различных файловых событий, таких как создание, изменение и удаление, а затем воздействовать на эти события общим способом.

Как пользоваться?
Создайте объект Path, представляющий каталог для отслеживания событий файла.

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

Реализуйте интерфейс 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();
            }
        }
    }

}

Создайте экземпляр реализованного FileHandler

FileHandlerTest fileHandlerTest = new FileHandlerTest();

Создайте экземпляр FileWatcher, передав путь, экземпляр реализованного FileHandler и типы событий файлов, которые вы хотите отслеживать, разделенные запятыми.

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

Теперь создайте и начните новую тему.

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

Этот поток начнет опрос ваших зарегистрированных файловых событий и вызовет ваш собственный метод дескриптора, как только будет обнаружено любое из зарегистрированных событий.

Ответ 6

открытый класс FileWatcher реализует 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();
        }
    }
}

}