获取当前文件长度/ FileInfo.Length缓存和陈旧信息

时间:2011-10-19 21:06:32

标签: c# .net file-io

我正在跟踪文件的文件夹及其文件长度,至少有一个文件仍在写入。

我必须不断更新每个文件长度的记录,我将其用于其他目的。

如果文件长度与上一次更新中确定的长度不同,则每15秒调用Update方法并更新文件的属性。

更新方法如下所示:

var directoryInfo = new DirectoryInfo(archiveFolder);
var archiveFiles = directoryInfo.GetFiles()
                                .OrderByDescending(f=>f.CreationTimeUtc); 
foreach (FileInfo fi in archiveFiles)
{
    //check if file existed in previous update already
    var origFileProps = cachedFiles.GetFileByName(fi.FullName);
    if (origFileProps != null && fi.Length == origFileProps.EndOffset)
    {
        //file length is unchanged
    }
    else
    {
        //Update the properties of this file
        //set EndOffset of the file to current file length
    }
}

我知道DirectoryInfo.GetFiles()预先填充了许多FileInfo属性,包括Length - 只要没有在之间进行缓存就可以了更新(缓存信息不应超过15秒)。

我假设每个DirectoryInfo.GetFiles()调用都会生成一组FileInfos,其中所有内容都填充了新信息,然后使用FindFirstFile / FindNextFile Win32 API。但事实似乎并非如此。

很少,但最终确定我遇到了一个文件的文件长度,一次写入的文件长度不会更新5,10或甚至20分钟(在Windows 2008 Server x64上进行测试)这很重要。)

目前的解决方法是调用fi.Refresh()强制更新每个文件信息。这内部似乎委托给GetFileAttributesEx Win32 API调用来更新文件信息。

虽然手动强制刷新的成本是可以忍受的,但我宁愿理解为什么我首先得到陈旧的信息。何时生成FileInfo信息以及它与DirectoryInfo.GetFiles()的调用有何关联?下面是否有文件I / O缓存层,我还没有完全掌握?

3 个答案:

答案 0 :(得分:15)

Raymond Chen现在写了一篇关于这个问题的非常详细的博文:

Why is the file size reported incorrectly for files that are still being written to?

  

在NTFS中,文件系统元数据是不属于目录条目的属性   而不是文件,一些元数据被复制到   目录条目作为改进目录枚举的调整   性能即可。 FindFirstFile等函数报告该目录   输入,并通过放置FAT用户习惯的元数据   得到“免费”,他们可以避免比FAT慢   目录列表。 目录枚举函数报告   最后更新的元数据,可能与实际元数据不对应   如果目录条目是陈旧的。

基本上归结为性能:从DirectoryInfo.GetFiles()和下面的FindFirstFile / FindNextFile Win32 API收集的目录信息由于性能原因而被缓存,以保证NTFS中的性能比在用于获取目录信息的旧FAT。只能通过直接调用文件Get­File­Size()(在Refresh()上的.NET调用FileInfo中或直接从文件名中获取FileInfo)来获取准确的文件大小信息 - 或打开和关闭文件流,使更新的文件信息传播到目录元数据高速缓存。后一种情况解释了为什么在写入过程关闭文件时立即更新文件大小。

这也解释了问题似乎没有出现在Windows 2003 Server中 - 当时刷新缓存时更频繁地复制文件信息 - 对于Windows 2008 Server不再是这种情况了:

  

至于频率,答案有点复杂。从...开始   Windows Vista(和我相应的Windows Server版本)   不知道,但我相信你可以抬头,而“你”我的意思是“雨虹   宝“),NTFS文件系统执行此礼貌复制时   关闭文件对象的最后一个句柄。 早期版本的NTFS   每当缓存时,文件都是打开时复制数据   脸红了,这意味着每隔一段时间就会发生一次   不可预测的时间表。这种变化的结果是   目录条目现在不经常更新,因此   最新更新的文件大小已经过时了。

阅读完整的文章非常有用,并建议使用!

答案 1 :(得分:5)

我认为您应该使用FileSystemWatcher并订阅Changed事件。更改指定的文件系统项时会触发它。

答案 2 :(得分:1)

我同意Wojteq使用FileSystemWatcher类是一个更好的解决方案。它公开了当文件或目录的不同属性发生变化时的事件(例如他引用的Change事件),并且它是比当前轮询解决方案更好的解决方案。要回答有关为什么Refresh花费不同时间来反映文件大小变化的问题,答案就是与Windows操作系统的底层虚拟内存管理器有关。当执行文件I / O时,它实际上对内存映射文件进行更新;这是由操作系统管理的文件的缓冲副本。因此,Windows控制何时将缓冲的数据写入磁盘。无法预测何时将特定的缓冲数据物理写入磁盘。这意味着更新文件流会将这些更新放在缓冲区中。如果你要Flush()流缓冲更新应该立即写入磁盘,如果你关闭流然后它将在流关闭后从缓冲区写入磁盘,如果流保持打开它是在Windows决定将缓冲的数据写入磁盘时。