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 7 WatchService 对其他人来说很慢吗?
Java 7 NIO watchservice vs jpathwatch