【问题标题】:Detecting moved files using FileSystemWatcher使用 FileSystemWatcher 检测移动的文件
【发布时间】:2009-08-17 04:10:00
【问题描述】:

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

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

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

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

根据反馈进行更新:

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

不幸的是,这些事件之间没有提供“链接”,因此如何区分文件移动和正常的删除或创建并不明显。在操作系统级别,一个移动被特殊处理,你可以几乎瞬间移动一个 1GB 的文件。

一些答案​​建议使用文件散列来在事件之间可靠地识别它们,我可能会采用这种方法。但是如果有人知道如何更简单地检测移动,请留下答案。

【问题讨论】:

  • 在同一个卷中移动的概念是一个低级的概念,从 Windows 上的 NTFS 开始,据我记得..所以它应该在某个地方..
  • @Aviad,是的,我希望它应该是一个低级的东西。如果我将一个 1 GB 文件从 C 盘上的一个文件夹移动到同一驱动器上的另一个文件夹,移动几乎是立即的。我猜 NTFS 只是在更新对同一文件表中文件的指针引用。令人讨厌的是它似乎没有扩展到 FileSystemWatcher,因此我的问题。
  • @Aviad Be Dov:不,至少可以追溯到 DOS 5.0,可能是 4

标签: c# filesystemwatcher


【解决方案1】:

根据docs

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

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

【讨论】:

  • @nader,是的,我可以为每个文件存储一个哈希,因此我可以将其用作指纹。因此,在我的工作队列中,我可以检查 Delete 事件并等待同一文件上的后续 Create 事件(保证),但是在将其视为简单的 Delete 事件之前要等待多长时间才能等待此后续 Create 事件?这比预期的要困难。
  • 你能在删除事件中获取文件和驱动器的大小吗?如果是这样,您可以使用此信息来决定。在同一个驱动器中的移动通常是即时的。所以可能是 5 秒。但是对于不同的驱动器,它与复制一样慢,根据大小来决定。显然不容易,但可以说很有趣:)
  • @ash,您可能需要测试几个不同的场景才能为该问题找到一个好的答案。正如丝绸所说,在驱动器内非常快(更改有关文件/文件夹的一些元数据),而在驱动器之间需要进行复制(这可能需要时间)。一个问题是,删除是立即发生(一旦你移动),还是仅在复制发生后(在后一种情况下,删除/创建可能相距不远,在前者中,你'得等一会儿)。
  • @nader,@silky,感谢您的反馈。是的,我认为我只需要对此进行一些测试。您还帮助我确认了我的处理方法。
【解决方案2】:

据我了解,Renamed 事件是针对正在移动的文件...?

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

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

它还提到了移动文件:

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

【讨论】:

  • 文档专门说“文件或目录”...也许我遗漏了什么?
  • @Aviad,我的应用程序中的文件移动会生成一个删除和创建事件,其中已删除文件的完整文件路径传递给 Delete 事件,新创建文件的完整路径传递到创建事件。在这个场景中我没有收到重命名事件。
  • 有吗?我希望您从原始路径中获得 OnRenamed 事件,其 Args 指向新路径..
  • “操作系统和 FileSystemWatcher 对象将剪切和粘贴操作或移动操作解释为文件夹及其内容的重命名操作”。这是在“复制和移动文件夹”部分下。看不到重命名的地方指的是文件。有人测试过吗?
  • @nader:刚刚也将它添加到答案中 - 查找并找到它.. :(
【解决方案3】:

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

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

另一种方法是创建一个微过滤器驱动程序来跟踪文件系统操作并将它们转发到您的应用程序。使用它,您基本上可以获得有关文件发生情况的所有信息,并且您将能够获得有关移动文件的信息。这种方法的一个缺点是您必须创建一个需要安装在目标系统上的单独驱动程序。不过好在你不需要从头开始,因为我已经开始创建类似这样的东西: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 上跟踪文件系统事件的信息。

【讨论】:

    【解决方案4】:

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

    您是否遇到过影响移动检测的随机文件创建案例?

    【讨论】:

    • @silky,我也是这么想的。该应用程序正在索引文件以进行搜索。这些文件可以由用户手动移动,或者只是将新文件添加(创建)到文件夹树中。也没有什么可以阻止用户创建一个名称完全相同但位于不同监视文件夹中的文件。
    • 好的,那我也可以考虑散列方案。这样,如果您注意到一个具有相同名称的新文件;您可以简单地比较大小,如果大小相同,则计算散列并进行比较。这将使您在合理的程度上知道(足够合理,考虑到索引,因为如果它具有相同的内容,那么基本上可以认为它“移动”:P
    • 想一想:如果您要使用哈希方案,您也可以像这样捕获“复制”文件。
    • 谢谢,这正是我打算做的。然而,让 FileSystemWatcher 报告这个会很方便,哦,好吧。有些文件可能很大(> 50MB),所以它可以节省做哈希计算。我可能需要查看某种 CRC,因为我相信这些可以比散列更快。
    • 请记住,您不需要一直进行散列;所以这应该是一个相当微不足道的成本。
    【解决方案5】:

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

    【讨论】:

    • @andymeadows,移动文件似乎会为原始文件生成 Delete 事件,并为新位置中的同一文件生成 Create 事件。我没有看到任何 Changed 和 Renamed 事件。
    • 您想做什么来保证文件的输入或重定位以便在两个不同的地方进行处理?我通常会从输入 -> 处理 -> 处理/错误中移动文件,并且在不了解为什么您的设计需要这样做的情况下提供更多建议时犹豫不决。
    • 这是一个搜索索引应用程序,例如,它可能会监视用户“我的文档”文件夹树中的所有 DOC 文件。用户可以在此树中手动移动文件,或将文件移入和移出此树。他们当然也可以简单地删除文件。区分这些场景对于避免不必要的文件重新索引很有用。
    • 可以使用文件大小和创建日期的组合来为文档生成哈希,因为名称可以更改。这不会是真正独一无二的,您必须根据预测的使用情况来扩展它。我敢肯定,您可以结合其他元数据来制作唯一键。我刚刚翻页,这也与丝绸所说的一致。您必须同时观察这两个事件并为您的哈希找到一些唯一值。
    • 检测移动的重点是避免重新执行昂贵的操作,如索引和散列。这里的重点是找到一种方法来知道文件已移动,而无需重新计算散列,因此存储散列是没有意义的,因为您是本末倒置……即计算新文件的散列以确定它是同一个文件...而不是知道它是同一个文件,因此您不必重新计算哈希。
    【解决方案6】:

    StorageLibrary 类can track moves。来自微软的例子:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-12-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多