我正在跟踪文件的文件夹及其文件长度,至少有一个文件仍在写入。
我必须不断更新每个文件长度的记录,我将其用于其他目的。
如果文件长度与上一次更新中确定的长度不同,则每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缓存层,我还没有完全掌握?
答案 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。只能通过直接调用文件GetFileSize()
(在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决定将缓冲的数据写入磁盘时。