Java WatchService 在观看映射驱动器时不生成事件

Posted

技术标签:

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

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

【问题讨论】:

【参考方案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

【讨论】:

以上是关于Java WatchService 在观看映射驱动器时不生成事件的主要内容,如果未能解决你的问题,请参考以下文章

java 使用Java WatchService观察目录

Java 7 WatchService 对其他人来说很慢吗?

Java 7 NIO watchservice vs jpathwatch

使用 Java WatchService 监视文件夹中的文件夹

Java 文件系统监控(WatchService)

WatchService 可能存在不能再window工作