【问题标题】:How to tell a file is *completely* written如何判断一个文件是*完全*写的
【发布时间】:2015-03-26 15:55:25
【问题描述】:

我熟悉 FileSystemWatcher 类,并且已经使用它进行了测试,或者我已经使用快速循环进行了测试,并在目录中列出了类型文件的目录列表。在这种特殊情况下,它们是 zip 压缩的 SDF 文件,我需要解压缩、打开和查询。

问题是当一个大文件被放到一个目录中时,有时会花费一些时间,比如它被下载,或者从网络位置复制等等......

当 FileSystemWatcher 引发 OnChange 事件时,我有一个 ChangeType 句柄,并且在这些类型的操作中,创建是立即的,而文件仍未完全复制到该位置。

同样使用循环,我看到一个文件在整个文件之前。

FileSystemWatcher 引发了几个更改事件,一个在创建之后,然后一个或多个在复制期间,没有任何说明 这个文件现在已经完成

因此,如果我期望某种类型的文件最终被放置在一个目录中以进行读取和处理,而不知道它们的传输机制,也不知道它们的最终大小......

除了使用错误控制作为工作流控制之外,我如何知道文件何时准备好实际处理(尽管错误控制本来就应该存在)?这似乎是一种处理这个问题的坏方法,因为有时错误控制实际上可能代表一个合法问题,有时可能只是文件没有完全写入,我看不到任何真正安全的区分方法.

我鄙视预期的错误,但意识到它就像套接字一样有它的位置,没有什么能保证在尝试读/写之前对打开的检查不会改变。但我确实不惜一切代价避免它。

这个特别的问题主要是因为将要产生的信息模棱两可。对于由于没有完全遇到或以其他方式损坏而合法错误的文件有一个冲突队列,我不希望其他好的文件去那里。几乎不可能更细致地检测这种特定情况。

编辑: 我知道我可以做到这一点......而且我已经阅读了其他关于其他人做同样事情的 SA 文章。 (而且我知道这种方法既粗暴又阻塞,这只是一个例子。)

private static void OnChanged(object source, FileSystemEventArgs e)
{
    if (e.ChangeType == WatcherChangeTypes.Created)
    {
        bool ready = false;
        while (!ready)
        {
            try
            {
                using (FileStream fs = new FileStream(e.FullPath, FileMode.Open))
                {
                    Console.WriteLine(String.Format("{0} - {1}", e.FullPath, fs.Length));
                }
                ready = true;
            }
            catch (IOException)
            {
                ready = false;
            }
        }
    }
}

我试图找出这绝对是唯一的方法,是否没有其他组件,或者文件系统的某些挂钩可以通过适当的事件实际执行此操作?

【问题讨论】:

  • 你可以做 pstools 句柄所做的 - 看看是否有人打开了文件(注意:我不知道句柄是如何工作的)。你当然可以使用句柄本身
  • 我已经考虑过一个循环,它只会用打开的请求来填充文件,直到它不再抛出 IO 异常,然后认为它至少不再被使用。句柄的问题是我不知道 expect 可能有打开的句柄,有些不会干扰我需要做的事情。 Exempli gratia,一个 AV 产品或索引器可能正在扫描它,但我仍然可以按预期使用它。因此,handles 只告诉我是否有其他东西在看它,而不是它是否为我准备好了。
  • 是的,在开始发布之前,我已经阅读了该文章和其他几篇 SA 文章,例如该主题有很多建议,但没有明确的示例答案,我选择更清楚地说明我的具体问题,而不是比坟墓挖那个。同样,我可能没有独占访问权限,因此我不能将其作为解决方案。我只需要可用的访问权限。
  • 我想您可以使用 LastWrite 通知过滤器并进行日期比较。如果正在写入文件,则 LastWrite 应保持递增。

标签: c# file-io filesystemwatcher


【解决方案1】:

唯一的判断方法是使用FileShare.Read 打开文件。如果进程仍在写入文件并且尚未关闭它,那将始终失败。否则,根本没有机制可以知道哪个特定进程正在执行写入操作,FSW 在文件系统设备驱动程序级别运行,并且不知道什么进程正在执行操作。可能不止一个。

第一次尝试时通常会失败,FSW 非常有效。一般来说,您不知道该过程将花费多少时间,这当然取决于它的编写方式,并且可能会使文件打开一段时间。可能是几小时或几天,一个日志文件就是一个例子。

所以你需要一个重试机制,它应该有一个指数退避算法来增加尝试之间的重试延迟。例如,以半秒延迟启动它,并在失败时继续增加延迟。这需要在工作线程中完成,而不是 FSW 回调。使用线程安全队列将文件的路径从 FSW 回调传递给工作线程。通常也是处理您收到的多个 FSW 通知的好策略。

注意启动效果,您当然在开始运行之前错过了任何通知,因此可能有大量文件正在等待工作。并注意 Heisenbugs,无论您对文件做什么都可能导致另一个进程崩溃。就像这个过程对你所做的一样:)

考虑使用任务调度程序定期运行的批处理式程序可能是更简单的替代方案。

【讨论】:

    【解决方案2】:

    对于一个极端,您可以使用文件系统微型过滤器驱动程序,它在最低级别分析文件的所有活动(并与用户模式应用程序通信)。 前段时间我写了一个概念验证迷你过滤器来检测 MS Office 文件转换。见下文。这样,您就可以可靠地检查文件的每个打开句柄。

    但是:即使这对你的问题也不是通用的解决方案:

    考虑:

    理论上,一个工具(例如 FTP 文件传输)可以写入文件的一部分,关闭它,然后再次打开它以添加新数据。这看起来很奇怪,但你不能可靠地只检查“没有更多打开的文件句柄”==>“文件现在准备好了”

    Alex K. 在他的评论中提供了一个很好的链接,我自己会使用类似于 Jon (https://stackoverflow.com/a/4278034/4547223) 的答案的解决方案

    如果时间不重要(您可以浪费几秒钟的时间来做决定):

    • 定期计时器(1 秒似乎合理)
    • 在每个计时器滴答声中检查文件大小
    • 如果文件大小没有增加,例如10 秒后也没有 FSWatcher 更改事件,尝试打开它。如果您意识到大小增量不均匀或非常缓慢,您可以即时调整“等待时间”。

    您的最大优势是您只处理 ZIP 文件,您有机会检测到由于“校验和无效”而导致的无效(不完整)文件

    我不希望官方有办法检测到这一点,因为没有“文件完全写入”的普遍概念。

    文件系统微型过滤器

    这可能就像是解决问题的大锤。

    前段时间,我需要解决 Office 2010 中的一个奇怪错误,即在 Office 文件转换期间它不会复制 ADS 元数据(文件分类需要 ADS)。我们与微软工程师讨论过这个问题(微软不愿意修复这个错误),他们遵守了我们的过滤驱动解决方案(最后,这被停止了,因为企业更喜欢手动解决方法。)

    尽管如此,如果有人真的想检查这是否是一个可能的解决方案:

    我已经写了步骤说明:

    https://stackoverflow.com/a/29252665/4547223

    【讨论】:

      猜你喜欢
      • 2022-11-22
      • 1970-01-01
      • 1970-01-01
      • 2013-07-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多