如何告诉文件是*完全*写

时间:2015-03-26 15:55:26

标签: c# file-io filesystemwatcher

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

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

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

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

FileSystemWatcher引发了几个更改事件,一个在创建之后,然后在复制期间一个或多个,没有任何内容显示此文件现已完成

所以如果我期待一个类型的文件,最终要放在一个目录中进行阅读和处理,不知道它们的传输机制,也不知道它们的最终大小...

除了使用错误控制作为工作流程控件之外,我怎么知道文件何时准备好 处理(尽管错误控制仍然存在)?这似乎是一种必须处理这种情况的坏方法,因为有时错误控制实际上可能代表一个合法的问题,有时它可能只是文件没有完全写入,我没有看到任何真正安全的区分方法

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

这个特别困扰我的主要是因为将要产生的消息含糊不清。对于合法错误的文件存在冲突队列,因为它们没有完全碰到或者是其他损坏的,我不希望其他好的文件到那里。更加精细地检测这种特定情况几乎是不可能的。

编辑: 我知道我可以做到这一点......而且我已经阅读了其他有关其他人做同样事情的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;
            }
        }
    }
}

我想要找到的是这绝对是唯一的方法,是否没有其他组件或文件系统的某些挂钩实际上会通过适当的事件执行此操作?

2 个答案:

答案 0 :(得分:3)

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

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

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

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

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

答案 1 :(得分:0)

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

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

考虑:

一个工具(例如FTP文件传输)理论上可以写入文件的一部分,关闭它,然后再次重新打开它以附加新数据。这似乎很好奇,但你不能可靠地检查“没有更多的打开文件句柄”==> “文件已准备就绪”

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

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

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

您的最大优势是您只处理ZIP文件,因为“校验和无效”,您有可能检测到无效(不完整)文件

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

文件系统迷你过滤器

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

前一段时间,我曾要求在Office 2010中解决一个奇怪的错误,它在办公室文件转换期间不会复制ADS元数据(文件分类需要ADS)。我们与微软的工程师讨论过这个问题(MS不愿意修复这个问题),他们遵守我们的过滤器驱动程序解决方案(最后,由于业务首选手动解决方案,因此停止了。)

然而,如果某人真的想检查这是否是一个可能的解决方案:

我已经写了一些步骤的解释:

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