【问题标题】:Java WatchService not generating events while watching mapped drivesJava WatchService 在观看映射驱动器时不生成事件
【发布时间】:2012-01-18 13:42:25
【问题描述】:

我实现了一个文件观察器,但我注意到 java nio 文件观察器不会为在映射驱动器上复制的文件生成事件。例如,我在 Unix 上运行文件观察器来观察映射到 Windows (H:\) 上的本地目录 (/sharedfolder),然后我在此目录 (H:\) 中放置了一个文件,但是文件观察器没有生成任何事件。现在,如果我在 Windows 上运行文件观察程序来观察映射驱动器 (H:\),它指的是一个 unix 路径 (/sharedfolder),并且从 unix 我把一个文件放在这个文件夹中,文件观察程序会识别更改并生成一个事件。它看起来像一个错误,或者我可能遗漏了一些东西,有什么想法吗?

【问题讨论】:

    标签: java file nio mapped-drive watchservice


    【解决方案1】:

    我在尝试通过 CIFS 观看已安装的 Windows 共享时遇到了同样的问题。 似乎无法获得filesystem events for CIFS mounts

    Java 7 NIO FileWatcher 的 linux 实现使用 inotify。 Inotify 是一个 linux 内核子系统,用于通知文件系统更改,非常适合本地目录,但显然不适用于 CIFS mounts

    在甲骨文,修复this bug 的优先级似乎并不高。 (这是他们的责任吗?更多的是操作系统问题......)

    JNotify 在 linux 系统上也使用 inotify,所以这也不是选项。

    不幸的是,映射驱动器监控似乎仅限于轮询器:

    • Apache VFS DefaultFileMonitor 轮询目录(挂载共享)
    • 基于标准 Java API 的文件轮询器。
    • 带有jCIFS 的自定义文件轮询器(因此共享不需要安装在主机上)

    我可能会尝试使用 Apache VFS Monitor,因为它可以检测文件的创建、更新和删除。它需要挂载共享,但这让操作系统负责 CIFS 连接,而不是我的应用程序。

    【讨论】:

      【解决方案2】:

      JDK 中的文件监视功能依赖于平台,因为它使用本地库,因此它在不同平台上的行为可能不同。我很惊讶它完全适用于网络驱动器 - Windows 必须轮询网络映射驱动器以进行更改,而 Linux 没有(我应该说是正确的)。

      通常这种监控在操作系统内核中实现,它显然知道哪些文件在本地被修改/创建/等,但是操作系统没有简单的方法来知道网络驱动器上发生的事情,因为它没有独占控制权它。

      【讨论】:

      • 是的,但是我正在查看的目录与我正在运行我的监视程序的机器上。因此,无论是通过网络还是在本地机器上进行传输,操作系统都应该知道这一点,否则如何进行传输。 windows 轮询映射驱动器是有道理的,但我不明白 Unix 是如何不知道通过网络对本地文件夹所做的更改。
      • @Ramcis:在 Linux 上,网络共享是通过 NFS 挂载的,而 NFS 在设计上是一种无状态协议。因此,服务器根本不知道有多少客户端正在访问什么文件。该设计有一些优点(例如开销较少)和一些缺点......
      【解决方案3】:

      我遇到了同样的问题。我已经通过在主类中创建一个新线程并定期触摸文件来解决它,以便触发一个新的更改事件。

      示例每 10 秒轮询一次目录。

      package com.ardevco.files;
      
      import java.io.IOException;
      import java.nio.file.DirectoryStream;
      import java.nio.file.Files;
      import java.nio.file.Path;
      import java.nio.file.Paths;
      import java.nio.file.attribute.FileTime;
      import java.util.ArrayList;
      import java.util.List;
      
      public class Touch implements Runnable {
      
          private Path touchPath;
      
          public Touch(Path touchPath) {
              this.touchPath = touchPath;
              this.checkPath = checkPath;
      
          }
      
          public static void touch(Path file) throws IOException {
              long timestamp = System.currentTimeMillis();
              touch(file, timestamp);
          }
      
          public static void touch(Path file, long timestamp) throws IOException {
              if (Files.exists(file)) {
                  FileTime ft = FileTime.fromMillis(timestamp);
                  Files.setLastModifiedTime(file, ft);
              }
          }
      
          List<Path> listFiles(Path path) throws IOException {
              final List<Path> files = new ArrayList<>();
              try (DirectoryStream<Path> stream = Files.newDirectoryStream(path)) {
                  for (Path entry : stream) {
                      if (Files.isDirectory(entry)) {
                          files.addAll(listFiles(entry));
                      }
                      files.add(entry);
                  }
              }
              return files;
          }
      
          @Override
          public void run() {
              while (true) {
                  try {
                      for (Path path : listFiles(touchPath)) {
                          touch(path);
                      }
                  } catch (IOException e) {
                      System.out.println("Exception: " + e);
                  }
      
                  try {
                      Thread.sleep(10000L);
                  } catch (InterruptedException e) {
                      System.out.println("Exception: " + e);
                  }
              }
      
          }
      
      }
      

      【讨论】:

        【解决方案4】:

        我在使用 Python 脚本监视远程 Windows 目录上的日志文件内容时遇到了类似的问题。

        这是我的答案。

        从 Unix 映射远程驱动器时,在 /etc/fstab 中使用 //xxx.xxx.xxx.xxx/shareddrive /media/shareddrive cifs username=xxxx,password=xxxx,**directio** 0 0

        您可以使用凭证文件来避免使用纯文本密码。

        命令可能会根据 unix 版本而改变,这是在 debian 下测试的。 它应该按预期工作。 你能告诉我它是否有效吗? 我计划在 Java 中实现相同的东西,所以答案可能对我也有用。

        【讨论】:

        • 这不再有效。我认为它曾经以一种有点不稳定的方式工作(无论如何在 ubuntu 中),但是在最近的更新之后它完全停止了工作。我添加了一个答案来描述我糟糕的解决方法。
        【解决方案5】:

        我也遇到了这个问题,并得出了与这里的其他人相同的结论(CIFS + inotify = 不行)。

        但是,由于我的工作流程碰巧依赖于远程挂载和依赖 inotify 的自动编译工具,所以我最终构建了一个(相当绝望和 hacky)解决方案,它基本上只是使用轮询来监视更改,然后触及同样的文件再次出现在挂载端,确实似乎触发了 inotify 事件。这不是我最自豪的时刻。

        话虽如此,它确实有效,所以,享受吧:http://github.com/rubyruy/watchntouch

        【讨论】:

          猜你喜欢
          • 2014-09-04
          • 1970-01-01
          • 2019-07-28
          • 1970-01-01
          • 2016-01-31
          • 2015-01-13
          • 2017-10-03
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多