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

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

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

Update方法每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缓存层,我没有完全掌握?

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

为什么报告的文件的文件大小不正确?

在NTFS中, 文件系统元数据不是目录条目的属性,而是文件的属性,其中一些元数据作为调整复制到目录条目中以提高目录枚举性能 。 像FindFirstFile这样的函数报告目录条目,并且通过放置FAT用户习惯于“免​​费”获取的元数据,它们可以避免比目录列表的FAT慢。 directory-enumeration函数报告上次更新的元数据,如果目录条目是陈旧的,则可能与实际元数据不对应。

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

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

至于频率,答案有点复杂。 从Windows Vista开始(及其相应的Windows Server版本,我不知道但我确定你可以查找,并且“你”我的意思是“Yuhong Bao”),NTFS文件系统执行这种礼貌复制时关闭文件对象的最后一个句柄。 早期版本的NTFS复制数据,而文件在刷新缓存时打开,这意味着它根据不可预测的时间表经常发生。 此更改的结果是目录条目现在不经常更新,因此最后更新的文件大小比现在更新。

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

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

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