使用FileSystemWatcher检测移动的文件

我意识到FileSystemWatcher不提供Move事件,而是为同一个文件生成单独的Delete和Create事件。 (FilesystemWatcher正在观察源文件夹和目标文件夹)。

但是,我们如何区分真正的文件移动和一些文件的随机创建,这些文件恰好与最近删除的文件同名?

FileSystemEventArgs类的某种属性,例如“AssociatedDeleteFile”,如果它是移动的结果,则分配已删除的文件路径,否则为NULL,这将是很好的。 但当然这不存在。

我也理解FileSystemWatcher在基本的文件系统级别运行,因此“移动”的概念可能仅对更高级别的应用程序有意义。 但如果是这种情况,人们会建议在我的应用程序中使用什么样的算法来处理这种情况?

根据反馈更新:

FileSystemWatcher类似乎看到将文件移动为2个不同的事件,删除原始文件,然后在新位置创建。

遗憾的是,这些事件之间没有提供“链接”,因此如何区分文件移动和普通删除或创建并不明显。 在操作系统级别,移动是专门处理的,您可以几乎立即移动说1GB文件。

一些答案建议在文件上使用哈希来可靠地在事件之间识别它们,我将合理地采用这种方法。 但如果有人知道如何更简单地发现一个动作,请留下答案。

根据文件 :

常见的文件系统操作可能会引发多个事件。 例如,当文件从一个目录移动到另一个目录时,可能会引发几个OnChanged和一些OnCreated和OnDeleted事件。 移动文件是一项复杂的操作,由多个简单操作组成,因此可以引发多个事件。

因此,如果您在检测移动时要非常小心,并且具有相同的路径并不够好,则必须使用某种启发式方法。 例如,使用文件名,大小,上次修改时间等为源文件夹中的文件创建“指纹”。 当您看到任何可能表示移动的事件时,请检查新文件的“指纹”。

据我所知, Renamed事件是针对正在移动的文件……?

我的错误 – 文档特别指出,在剪切和粘贴操作中,只有被移动的文件夹中的文件被视为“重命名”:

操作系统和FileSystemWatcher对象将剪切和粘贴操作或移动操作解释为文件夹及其内容的重命名操作。 如果将包含文件的文件夹剪切并粘贴到正在监视的文件夹中,则FileSystemWatcher对象仅将该文件夹报告为新文件夹,而不报告其内容,因为它们基本上仅重命名。

它还说移动文件:

常见的文件系统操作可能会引发多个事件。 例如,当文件从一个目录移动到另一个目录时,可能会引发几个OnChanged和一些OnCreated和OnDeleted事件。 移动文件是一项复杂的操作,由多个简单操作组成,因此可以引发多个事件。

我会冒险猜测’移动’确实不存在,所以你真的只需要寻找’删除’,然后将该文件标记为可能’可能被移动’,然后如果你看到不久之后我就会“创造”,我想你可以认为你是对的。

您是否有一个随机文件创建案例影响您对移动的检测?

可能想尝试文档中提到的OnChanged和/或OnRenamed事件。

正如您已经提到的,使用C#提供的默认FileSystemWatcher类没有可靠的方法。 您可以应用某些启发式方法(如文件名,哈希值或唯一文件ID)将创建和删除的事件映射到一起,但这些方法都不会可靠地运行。 此外,您无法轻松获取与已删除事件关联的文件的哈希或文件ID,这意味着您必须在某种数据库中维护这些值。

我认为检测文件移动的唯一可靠方法是创建自己的文件系统观察器。 因此,您可以使用不同的方法。 如果您只想在NTFS文件系统上观察更改,一种解决方案可能是读取NTFS更改日志,如此处所述。 这有什么好处,它甚至可以让你跟踪你的应用程序没有运行时发生的变化。

另一种方法是创建一个跟踪文件系统操作并将它们转发到您的应用程序的minifilter驱动程序。 使用此function,您基本上可以获得有关文件发生情况的所有信息,并且您将能够获得有关已移动文件的信息。 此方法的缺点是您必须创建需要在目标系统上安装的单独驱动程序。 然而,好处是你不需要从头开始,因为我已经开始创建这样的东西: https : //github.com/CenterDevice/MiniFSWatcher

这使您可以像这样简单地跟踪移动的文件:

 var eventWatcher = new EventWatcher(); eventWatcher.OnRenameOrMove += (filename, oldFilename, process) => { Console.WriteLine("File " + oldFilename + " has been moved to " + filename + " by process " + process ); }; eventWatcher.Connect(); eventWatcher.WatchPath("C:\\Users\\MyUser\\*"); 

但请注意,这需要在64位版本的Windows上运行时需要签名的内核代码(如果不禁用签名检查以进行测试)。 在撰写本文时,此代码仍处于开发的早期阶段,因此我还不会在生产系统上使用它。 但即使您不打算使用它,它仍应提供有关如何在Windows上跟踪文件系统事件的一些信息。