Java 中文件目录监听实现(java监听ftp目录)

目录监听实现目前主要有两种实现,一种是 java 7 自带的 API ,另一种是 apache commons-io 类库实现。

SDK watch监控

采用 SDK API watch 实现目录变化。

  • 可以监听目录下的文件新增、删除、变更
  • 目录下文件名变更会收到一条删除,一条新增消息
  • 目录下的子目录可以得到监听,即子目录下的文件发生新增、删除会得到目录变更的通知
  • 只能得到目录发生了变化的通知,无法知道目录下那些文件发生了变化
  • 子目录下的文件内容发生变化不会得到消息通知

注:com.xxx.common.util.Assert 可以用 apache-common 中的,功能实现一样

  import com.xxx.common.util.Assert;
 import lombok.extern.slf4j.Slf4j;
 
 import java.io.IOException;
 import java.nio.file.*;
 import java.util.concurrent.TimeUnit;
 
 import static java.nio.file.StandardWatchEventKinds.*;
 
 /**
  * java 7 实现文件目录监听,能够实现文件目录的新增、变更、删除;
  *  1、修改文件名时,会收到一次删除,一次新增事件
  *  2、可以监控子目录变化,但无法监控子目录中文件的变化(子目录下文件内容发生变化时不会触发监听事件)
  *
  * @author: zhangql
  * @create: 2023/3/17 9:08
  **/
 @Slf4j
 public class SdkDirectoryWatch implements Runnable {
 
     private String fileDirectory;
 
     public SdkDirectoryWatch(String fileDirectory) {
         Assert.notEmpty(fileDirectory, "fileDirectory is null");
 
         this.fileDirectory = fileDirectory;
     }
 
     @Override
     public void run() {
         WatchService watchService = null;
 
         try {
             watchService = FileSystems.getDefault().newWatchService();
 
             //监听文件目录
             Paths.get(fileDirectory)
                     //注册监听事件
                     .register(watchService, ENTRY_CREATE, ENTRY_DELETE, ENTRY_MODIFY);
         }
         catch (IOException e) {
             log.error("注册监听器异常", e);
         }
 
         while(!Thread.currentThread().isInterrupted()) {
             WatchKey watchKey = null;
             try {
                 watchKey = watchService.take();
 
             }
             catch (InterruptedException e) {
                 log.warn("线程被中断,退出监听");
 
                 break;
             }
 
             for (WatchEvent<?> pollEvent : watchKey.pollEvents()) {
                 log.debug("{} 文件: {}, 次数:{}", pollEvent.context(), pollEvent.kind(), pollEvent.count());
             }
 
             //重置状态(当前 Key 就不会再获取将来发生的事件)
             boolean valid = watchKey.reset();
             //失效状态,退出监听
             if (!valid) {
                 break;
             }
         }
 
     }
 
     public static void main(String[] args) throws InterruptedException {
         String directory = "D:\\Temp\\images";
         Runnable runnable = new SdkDirectoryWatch(directory);
 
         Thread thread = new Thread(runnable);
         thread.start();
 
         Thread.sleep(TimeUnit.SECONDS.toMillis(30));
         thread.interrupt();
     }
 }

控制台信息

 10:39:31.623 [Thread-0] DEBUG com.xxx.base.directory.watch.SdkDirectoryWatch - ~建 XLSX 工作表.tmp 文件: ENTRY_CREATE, 次数:1
 10:39:31.624 [Thread-0] DEBUG com.xxx.base.directory.watch.SdkDirectoryWatch - ~建 XLSX 工作表.tmp 文件: ENTRY_MODIFY, 次数:1
 10:39:31.625 [Thread-0] DEBUG com.xxx.base.directory.watch.SdkDirectoryWatch - ~建 XLSX 工作表.tmp 文件: ENTRY_MODIFY, 次数:1
 10:39:31.625 [Thread-0] DEBUG com.xxx.base.directory.watch.SdkDirectoryWatch - ~建 XLSX 工作表.tmp 文件: ENTRY_MODIFY, 次数:2
 10:39:31.626 [Thread-0] DEBUG com.xxx.base.directory.watch.SdkDirectoryWatch - ~建 XLSX 工作表.tmp 文件: ENTRY_MODIFY, 次数:1
 10:39:31.627 [Thread-0] DEBUG com.xxx.base.directory.watch.SdkDirectoryWatch - ~建 XLSX 工作表.tmp 文件: ENTRY_MODIFY, 次数:1
 10:39:31.627 [Thread-0] DEBUG com.xxx.base.directory.watch.SdkDirectoryWatch - 新建 XLSX 工作表.xlsx~RFab854c4.TMP 文件: ENTRY_CREATE, 次数:1
 10:39:31.629 [Thread-0] DEBUG com.xxx.base.directory.watch.SdkDirectoryWatch - 新建 XLSX 工作表.xlsx~RFab854c4.TMP 文件: ENTRY_DELETE, 次数:1
 10:39:31.629 [Thread-0] DEBUG com.xxx.base.directory.watch.SdkDirectoryWatch - 新建 XLSX 工作表.xlsx 文件: ENTRY_DELETE, 次数:1
 10:39:31.629 [Thread-0] DEBUG com.xxx.base.directory.watch.SdkDirectoryWatch - 新建 XLSX 工作表.xlsx~RFab854c4.TMP 文件: ENTRY_CREATE, 次数:1
 10:39:31.630 [Thread-0] DEBUG com.xxx.base.directory.watch.SdkDirectoryWatch - ~建 XLSX 工作表.tmp 文件: ENTRY_DELETE, 次数:1
 10:39:31.630 [Thread-0] DEBUG com.xxx.base.directory.watch.SdkDirectoryWatch - 新建 XLSX 工作表.xlsx 文件: ENTRY_CREATE, 次数:1
 10:39:31.630 [Thread-0] DEBUG com.xxx.base.directory.watch.SdkDirectoryWatch - 新建 XLSX 工作表.xlsx 文件: ENTRY_MODIFY, 次数:1
 10:39:31.630 [Thread-0] DEBUG com.xxx.base.directory.watch.SdkDirectoryWatch - 新建 XLSX 工作表.xlsx~RFab854c4.TMP 文件: ENTRY_DELETE, 次数:1

部份文件的变更会收到多条记录。比如在目录下创建一个 Excel 文件,收到的消息如上面消息所示。

Apache Commons-io 监控

FileListener

 package com.xxx.base.directory.watch.apache;
 
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.io.monitor.FileAlterationListenerAdaptor;
 import org.apache.commons.io.monitor.FileAlterationObserver;
 
 import java.io.File;
 
 /**
  * @author: zhangql
  * @create: 2023/3/17 9:47
  **/
 @Slf4j
 public class FileListener extends FileAlterationListenerAdaptor {
 
     public FileListener() {
         super();
     }
 
     @Override
     public void onStart(FileAlterationObserver observer) {
         super.onStart(observer);
     }
 
     @Override
     public void onDirectoryCreate(File directory) {
         log.debug("创建目录:{}", directory.getAbsolutePath());
     }
 
     @Override
     public void onDirectoryChange(File directory) {
         log.debug("变更目录:{}", directory.getAbsolutePath());
     }
 
     @Override
     public void onDirectoryDelete(File directory) {
         log.debug("删除目录:{}", directory.getAbsolutePath());
     }
 
     @Override
     public void onFileCreate(File file) {
         log.debug("创建文件:{}", file.getAbsolutePath());
     }
 
     @Override
     public void onFileChange(File file) {
         log.debug("文件变更:{}", file.getAbsolutePath());
     }
 
     @Override
     public void onFileDelete(File file) {
         log.debug("文件删除:{}", file.getAbsolutePath());
     }
 
     @Override
     public void onStop(FileAlterationObserver observer) {
         super.onStop(observer);
     }
 }
 

ApacheDirectoryWatch

 package com.xxx.base.directory.watch.apache;
 
 import org.apache.commons.io.filefilter.FileFilterUtils;
 import org.apache.commons.io.filefilter.HiddenFileFilter;
 import org.apache.commons.io.filefilter.IOFileFilter;
 import org.apache.commons.io.monitor.FileAlterationMonitor;
 import org.apache.commons.io.monitor.FileAlterationObserver;
 
 import java.io.File;
 import java.util.concurrent.TimeUnit;
 
 /**
  * 与 SDK 监听基本类似
  * @author: zhangql
  * @create: 2023/3/17 9:51
  **/
 public class ApacheDirectoryWatch {
 
     public static void main(String[] args) throws Exception {
         String directory = "D:\\Temp\\images";
         //轮询时间
         long interval = TimeUnit.SECONDS.toMillis(5);
 
         //目录过滤器
         IOFileFilter directoryFilter = FileFilterUtils.and(
                 FileFilterUtils.directoryFileFilter(), HiddenFileFilter.VISIBLE
         );
 
         //文件过滤器
         IOFileFilter fileFilter = FileFilterUtils.and(
                 FileFilterUtils.fileFileFilter(), FileFilterUtils.suffixFileFilter(".png")
         );
 
         IOFileFilter filter = FileFilterUtils.or(directoryFilter, fileFilter);
         //使用过滤器
         FileAlterationObserver observer = new FileAlterationObserver(new File(directory), filter);
         //不使用过虑器
         //FileAlterationObserver observer = new FileAlterationObserver(new File(directory));
         observer.addListener(new FileListener());
 
         //创建文件监听器
         FileAlterationMonitor monitor = new FileAlterationMonitor(interval, observer);
         monitor.start();
 
     }
 }
 

实现功能基本和 SDK API 相同,在 SDK API 基础上增加了文件类型的过滤。

控制台信息

 10:39:32.236 [Thread-0] DEBUG com.xxx.base.directory.watch.apache.FileListener - 创建文件:D:\Temp\images\新建 XLSX 工作表.xlsx
 10:40:05.988 [Thread-0] DEBUG com.xxx.base.directory.watch.apache.FileListener - 创建文件:D:\Temp\images\新建文本文档.txt

Apache Commons-io 采用轮询

相关文章

Java 中获取文件路径的方式,你知道几种?

1. 前言Java 开发中我们经常要获取文件的路径,比如读取配置文件等等。今天我们就关于文件的路径和如何读取文件简单地探讨一下。2. 文件的路径文件的路径通常有 相对路径 与 绝对路径。2.1 相对路...

java获取文件路径(java如何获取文件路径)

1. 前言Java 开发中我们经常要获取文件的路径,比如读取配置文件等等。今天我们就关于文件的路径和如何读取文件简单地探讨一下。2. 文件的路径文件的路径通常有 相对路径 与 绝对路径。2.1 相对路...

纯小白干货:Java import以及Java类的搜索路径

如果你希望使用Java包中的类,就必须先使用import语句导入。import语句与C语言中的 #include 有些类似,语法为: import package1[.package2…].class...

Java路径-31-Java数据结构(java数据结构详解)

1 枚举(Enumeration)1.1 Enumeration源码:public interface Enumeration<E> { boolean hasMoreEleme...

几种获取resources目录下的文件方式

前言一般我们的配置信息默认都是会配置在/src/main/resources/application.properties(或者application.yml)文件中,当然,也可以在resources...

「JAVA」三种方式加载本地资源文件:绝对路径、类路径、当前路径

三种方式加载本地资源文件:绝对路径、类路径、当前路径,本文以"db.properties"资源文件为例,分别介绍三种加载本地资源文件的方式。加载properties文件时,使用的是Pr...