获取当前文件长度/ FileInfo.长度缓存和过期信息

本文关键字:缓存 过期 信息 FileInfo 文件 获取 | 更新日期: 2023-09-27 18:11:39

我正在跟踪一个文件夹的文件和它们的文件长度,这些文件中至少有一个仍在被写入。

我必须保持每个文件长度的连续更新记录,我将其用于其他目的。

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

更新方法看起来像这样:

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缓存层?

获取当前文件长度/ FileInfo.长度缓存和过期信息

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

为什么仍在写入的文件报告的文件大小不正确?

在NTFS中,文件系统元数据是一个不属于目录条目的属性而不是文件,将一些元数据复制到目录条目作为改进目录枚举的调整性能。像Find-First-File这样的函数报告目录条目,并通过放入FAT用户习惯的元数据获得"免费",他们可以避免比FAT慢目录清单。目录枚举函数报告最近更新的元数据,可能与实际元数据不对应

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

这也解释了这个问题似乎没有出现在Windows 2003服务器上——那时文件信息被更频繁地复制/每当缓存被刷新时——这在Windows 2008服务器上不再是这种情况了:

至于多久一次,答案有点复杂。从Windows Vista(以及它对应的Windows Server版本)我不知道,但我相信你可以抬头看,我说的"你"是指"雨红", NTFS文件系统执行这种礼貌复制文件对象的最后一个句柄被关闭。 NTFS的早期版本当文件处于打开状态时,只要缓存处于打开状态,就复制数据脸红了,意思是经常会脸红不可预测的时间表。这种变化的结果是目录项现在更新的频率较低,因此

阅读全文是非常有益的,建议!

我认为您应该使用FileSystemWatcher并订阅Changed事件。

当指定的文件系统项发生变化时触发。

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