pywatchdog与pyinotify无法检测FTP创建目录内的文件变更问题
解决ProFTPD 1.3.5a下pywatchdog/pyinotify监控新创建FTP目录文件事件丢失的问题
我之前踩过类似的监控FTP服务器文件事件的坑,结合你提到的ProFTPD 1.3.5a版本和pywatchdog/pyinotify的表现,大概率是ProFTPD的目录创建机制和Linux inotify的默认行为不兼容导致的,下面给你拆解问题根源和可行的解决办法:
问题根源分析
首先得明确:pywatchdog在Linux平台底层就是基于pyinotify实现的,所以两者出现相同异常,说明问题出在系统或FTP服务层面,而非库本身:
- inotify的默认限制:inotify默认只监控你初始指定的路径,对后续动态创建的子目录不会自动触发递归监控——哪怕你一开始设置了递归,部分旧内核或特殊场景下也可能失效;
- ProFTPD的目录创建逻辑:ProFTPD 1.3.5a在通过FTP创建目录时,可能会使用特殊的权限设置或系统调用,导致新目录的inotify监控继承失败;另外,如果监控进程对新创建的目录没有
r-x权限,也无法捕获其下的文件事件。
可行解决方案
1. 调整pywatchdog配置,确保递归监控生效
如果你坚持用pywatchdog,一定要在初始化监控时明确开启recursive=True,并且在捕获到目录创建事件时手动补加监控(防止内核递归监控失效):
import time from watchdog.observers import Observer from watchdog.events import FileSystemEventHandler class FTPMonitorHandler(FileSystemEventHandler): def __init__(self, observer): self.observer = observer def on_created(self, event): # 处理文件创建事件 if not event.is_directory: print(f"捕获到新文件: {event.src_path}") # 新目录创建时,手动添加监控 else: print(f"捕获到新目录,添加监控: {event.src_path}") self.observer.schedule(FTPMonitorHandler(self.observer), event.src_path, recursive=True) def on_modified(self, event): if not event.is_directory: print(f"捕获到文件修改: {event.src_path}") if __name__ == "__main__": ftp_root = "/path/to/ftp/root" observer = Observer() # 关键:开启recursive=True observer.schedule(FTPMonitorHandler(observer), ftp_root, recursive=True) observer.start() try: while True: time.sleep(1) except KeyboardInterrupt: observer.stop() observer.join()
2. 修改ProFTPD配置,优化目录权限与行为
打开ProFTPD的主配置文件(通常是/etc/proftpd/proftpd.conf),调整以下配置:
- 确保目录权限允许监控进程读取:
Umask 022 022 DirUmask 022 022 - 禁用
UseSendfile(部分场景下会影响文件系统事件触发):UseSendfile off - 限制用户操作目录范围,避免权限交叉:
DefaultRoot /path/to/ftp/root
修改后重启ProFTPD服务:
sudo systemctl restart proftpd
3. 用pyinotify手动实现可靠的递归监控
如果pywatchdog的递归监控还是不稳定,可以直接用pyinotify手动处理目录创建事件,补加监控:
import pyinotify class FTPMonitorHandler(pyinotify.ProcessEvent): def __init__(self, watch_manager): self.wm = watch_manager # 监控事件类型:创建+修改 self.mask = pyinotify.IN_CREATE | pyinotify.IN_MODIFY def process_IN_CREATE(self, event): if event.dir: # 新目录创建,添加递归监控 self.wm.add_watch(event.pathname, self.mask, rec=True) print(f"添加新目录监控: {event.pathname}") else: print(f"捕获到文件创建: {event.pathname}") def process_IN_MODIFY(self, event): if not event.dir: print(f"捕获到文件修改: {event.pathname}") if __name__ == "__main__": ftp_root = "/path/to/ftp/root" wm = pyinotify.WatchManager() handler = FTPMonitorHandler(wm) notifier = pyinotify.Notifier(wm, handler) # 初始监控根目录 wm.add_watch(ftp_root, handler.mask, rec=True) notifier.loop()
额外验证步骤
- 检查监控进程的用户权限,确保它对FTP根目录及其所有子目录拥有
r-x权限; - 如果出现监控数量超限的错误,调整系统inotify上限:
echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf sudo sysctl -p - 手动在FTP根目录下创建目录并上传文件,验证事件是否被捕获。
内容的提问来源于stack exchange,提问作者Luiz Fernando Schrickte
相关产品推荐
相关产品推荐

