使用FileSystemWatcher检测移动的文件

时间:2009-08-17 04:10:01

标签: c# filesystemwatcher

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

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

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

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

根据反馈进行更新:

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

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

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

6 个答案:

答案 0 :(得分:12)

根据docs

  

通用文件系统操作可能   举办不止一场比赛。对于   例如,从一个文件移动文件时   目录到另一个,几个   OnChanged和一些OnCreated和   可能会引发OnDeleted事件。   移动文件是一项复杂的操作   它由多个简单组成   操作,因此提高了多个   事件

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

答案 1 :(得分:3)

据我了解,Renamed事件适用于要移动的文件......?

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

  

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

它还说移动文件:

  

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

答案 2 :(得分:2)

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

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

答案 3 :(得分:2)

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

答案 4 :(得分:2)

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

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

另一种方法是创建一个跟踪文件系统操作并将它们转发到您的应用程序的minifilter驱动程序。使用此功能,您基本上可以获得有关文件发生情况的所有信息,并且您将能够获取有关已移动文件的信息。此方法的缺点是您必须创建需要在目标系统上安装的单独驱动程序。然而,好处是你不需要从头开始,因为我已经开始创建这样的东西: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上运行时需要签名的内核代码(如果您不进行disable签名检查以进行测试)。在撰写本文时,此代码仍处于开发的早期阶段,因此我在生产系统上使用它。但即使您不打算使用它,它仍应提供有关如何在Windows上跟踪文件系统事件的一些信息。

答案 5 :(得分:0)

StorageLibrary类can track moves。 Microsoft的示例:

StorageLibrary videosLib = await StorageLibrary.GetLibraryAsync(KnownLibraryId.Videos);
StorageLibraryChangeTracker videoTracker = videosLib.ChangeTracker;
videoTracker.Enable();

可以找到完整的示例here。 但是,您似乎只能跟踪Windows“ known libraries”内部的更改。

您也可以尝试使用StorageFolder.TryGetChangeTracker()获取StorageLibraryChangeTracker。但是您的文件夹必须位于同步根目录下,不能使用此方法在文件系统中获取任意文件夹。