javaee论坛

普通会员

225648

帖子

344

回复

358

积分

楼主
发表于 2019-11-04 06:32:48 | 查看: 63 | 回复: 0

文章目录概述文件系统事件类文件系统事件处理类最简单的应用示例存在的问题改进方案

概述

首先声明,本文讨论的watchdog,不是单片机里的watchdog,也不是linux中的watchdog,而是python世界里用来监视文件系统变化的一个第三方模块。在python中文件监视主要有两个库,一个是pyinotify,一个是watchdog。pyinotify依赖于linux平台的inotify机制,只能应用在linux平台上。watchdog则对不同平台的的事件都进行了封装,不仅可以监视windows文件系统,也可以监视linux的文件系统。

文件系统事件类

文件系统事件基类定义如下:

watchdog.events.FileSystemEvent(event_type,src_path,is_directory=False)#event.event_type-事件类型,为moved/deleted/created/modified其中之一#event.src_path-触发该事件的文件或目录路径#event.is_directory-该事件是否由一个目录触发

由watchdog.events.FileSystemEvent基类派生的子类如下:

watchdog.events.FileDeletedEvent()#文件被删除时触发该事件watchdog.events.DirDeletedEvent()#目录被删除时触发该事件watchdog.events.FileCreatedEvent()#文件被创建时触发该事件watchdog.events.DirCreatedEvent()#目录被创建时触发该事件watchdog.events.FileModifiedEvent()#文件被修改时触发该事件(修改文件内容、修改文件inode信息如权限和访问时间,都会触发该事件)watchdog.events.DirModifiedEvent()#目录被修改时触发该事件watchdog.events.FileMovedEvent()#文件被移动或重命名时触发该事件,因为涉及文件移动,所以除了event.src_path表示原路径,还有event.dest_path表示目的路径watchdog.events.DirMovedEvent()#目录被移动或重命名时触发该事件,因为涉及文件移动,所以除了event.src_path表示原路径,还有event.dest_path表示目的路径文件系统事件处理类

watchdog.events.FileSystemEventHandler是事件处理器的基类,用于处理事件,用户需继承该类,并在子类中重写对应方法。需要用户重写的方法有:

self.on_any_event(event)#任何事件发生都会首先执行该方法,该方法默认为空,dispatch()方法会先执行该方法,然后再把event分派给其他方法处理self.on_moved(event)#处理DirMovedEvent和FileMovedEvent事件,默认为空self.on_created(event)#处理DirCreatedEvent和FileCreatedEvent事件,默认为空self.on_deleted(event)#处理DirDeletedEvent和FileDeletedEvent事件,默认为空self.on_modified(event)#处理DirModifiedEvent和FileModifiedEvent事件,默认为空

以上方法中,event有几个属性可用:

event.is_directory-触发事件的是否为文件夹event.src_path-源路径event.dest_path-目标路径最简单的应用示例

下面的例子展示了如何监视D:\XufiveGit\PEC\client文件夹内所有文件的moved/deleted/created/modified。请注意,重命名被视为moved(移动)。

#-*-coding:utf-8-*-fromwatchdog.observersimportObserverfromwatchdog.eventsimport*classFileEventHandler(FileSystemEventHandler):defon_any_event(self,event):passdefon_moved(self,event):ifevent.is_directory:print("directorymovedfrom{0}to{1}".format(event.src_path,event.dest_path))else:print("filemovedfrom{0}to{1}".format(event.src_path,event.dest_path))defon_created(self,event):ifevent.is_directory:print("directorycreated:{0}".format(event.src_path))else:print("filecreated:{0}".format(event.src_path))defon_deleted(self,event):ifevent.is_directory:print("directorydeleted:{0}".format(event.src_path))else:print("filedeleted:{0}".format(event.src_path))defon_modified(self,event):ifevent.is_directory:print("directorymodified:{0}".format(event.src_path))else:print("filemodified:{0}".format(event.src_path))if__name__=="__main__":importtimeobserver=Observer()event_handler=FileEventHandler()observer.schedule(event_handler,r"D:\XufiveGit\PEC\client",True)observer.start()try:whileTrue:time.sleep(1)exceptKeyboardInterrupt:observer.stop()存在的问题

真正测试过之后,你会发现,上面的例子几乎没有实用价值,因为,文件操作引发的事件比我们想象的多了不少,而且难以在事件函数中做出针对性处理。比如,添加一个文件,势必引发created事件,同时也会导致所在文件夹的modified事件,如果该文件目录比较深,还会引发多层父级文件夹的modified事件。

如果,你觉得这不算什么问题,那么,在windows平台上每一次的文件修改引发两次modified事件,算不算一个令人头疼的问题呢?在linux平台上表现如何,我没有测试过,但在windows平台上,由于watchdog封装的是windows系统的FileSystemWatcherEvents,处理文件的过程中执行了多次文件系统操作,无法避免地触发了多次事件。

改进方案

如果对监视文件的实时性要求不高,又懒得处理一大堆事件,那么,比较事件前后的文件夹快照就是一个值得尝试的改进方案。实现这个思路,有三个前提条件:

快速获取文件夹快照。幸运的是,watchdog模块为我们提供了DirectorySnapshot功能可以接受200毫秒的延时。文件操作引发的一大堆事件会集中在一个较短的时间内,一般情况下,在文件操作之后200毫秒获取文件夹快照,是一个不错的间隔快速比较文件夹快照。这也不是问题,watchdog模块有DirectorySnapshotDiff子模块

改进思路是这样的:设置一个定时器,200毫秒后抓取快照,并与上一张快照比较。每当有事件发生时,检查定时器是否已经启动。如果未启动,则直接启动定时器;否则,说明该事件距离上次事件不足200毫秒,可视为是同一组事件,此时终止定时器,再次重启。具体代码如下:

#-*-coding:utf-8-*-importos,threadingfromwatchdog.observersimportObserverfromwatchdog.eventsimport*fromwatchdog.utils.dirsnapshotimportDirectorySnapshot,DirectorySnapshotDiffclassFileEventHandler(FileSystemEventHandler):def__init__(self,aim_path):FileSystemEventHandler.__init__(self)self.aim_path=aim_pathself.timer=Noneself.snapshot=DirectorySnapshot(self.core.proj_path)defon_any_event(self,event):ifself.timer:self.timer.cancel()self.timer=threading.Timer(0.2,self.checkSnapshot)self.timer.start()defcheckSnapshot(self):snapshot=DirectorySnapshot(self.aim_path)diff=DirectorySnapshotDiff(self.snapshot,snapshot)self.snapshot=snapshotself.timer=Noneprint("files_created:",diff.files_created)print("files_deleted:",diff.files_deleted)print("files_modified:",diff.files_modified)print("files_moved:",diff.files_moved)print("dirs_modified:",diff.dirs_modified)print("dirs_moved:",diff.dirs_moved)print("dirs_deleted:",diff.dirs_deleted)print("dirs_created:",diff.dirs_created)#接下来就是你想干的啥就干点啥,或者该干点啥就干点啥passclassDirMonitor(object):"""文件夹监视类"""def__init__(self,aim_path):"""构造函数"""self.aim_path=aim_pathself.observer=Observer()defstart(self):"""启动"""event_handler=FileEventHandler(self.aim_path)self.observer.schedule(event_handler,self.aim_path,True)self.observer.start()defstop(self):"""停止"""self.observer.stop()if__name__=="__main__":monitor=DirMonitor(r"D:\XufiveGit\PEC\client")monitor.start()

您需要登录后才可以回帖 登录 | 立即注册

触屏版| 电脑版

技术支持 历史网 V2.0 © 2016-2017