与Windows XP相比,我注意到在Windows Server 2008上连续写入文件时会出现奇怪的行为。
我从几个线程执行类似的东西:
using (var fi = File.Open(...))
{
using (var fw = new StreamWriter(fi))
{
while program-is-running
{
fw.WriteLine(some-data);
fw.Flush();
}
}
}
当我在Total Commander和Windows资源管理器中观察文件时,我发现文件大小保持不变。我可以走进文件所在的目录,然后从目录中走出来,然后再次走进去 - 文件大小是恒定的(在Total Commander和Windows资源管理器中),直到我在Windows资源管理器中手动刷新目录内容 F5
当我在Windows XP中运行相同的程序时 - Total Commander和Windows Explorer连续显示文件大小正在增加。
我想知道问题是什么。是NTFS行为,软件设置还是其他什么?
提前谢谢!
答案 0 :(得分:3)
请参阅 The Old New Thing上的this article。基本上,它与效率有关。最新的文件元数据(例如其大小)不与目录条目一起存储,而是与文件本身一起存储(这类似于某些UNIX文件系统的工作方式,与旧的FAT方法相反)。
但是,大小会定期传输到目录条目,以便您可以获得“上次已知”值。并且,在某个时间点(文件停止更改之后),最后一个已知值变得准确。
从那篇文章:
在NTFS中,文件系统元数据不是目录条目的属性,而是文件的属性,其中一些元数据作为调整复制到目录条目中以提高目录枚举性能。像FindFirstFile这样的函数报告目录条目,并且通过放置FAT用户习惯于“免费”获取的元数据,它们可以避免比目录列表的FAT慢。 directory-enumeration函数报告上次更新的元数据,如果目录条目是陈旧的,则可能与实际元数据不对应。
下一个问题是此元数据复制的执行位置和频率;换句话说,这些数据有多陈旧?为了避免每次更改文件的元数据时都必须更新可能无限数量的目录条目,NTFS人员决定只从文件执行复制到用于打开文件的目录条目。这意味着如果文件有一千个硬链接,则文件大小的更改将反映在用于打开文件的目录条目中,但其他999目录条目将包含过时数据。
从Windows Vista开始(及其相应的Windows Server版本,我不知道,但我确定你可以查找,而“你”我的意思是“Yuhong Bao”),NTFS文件系统执行此礼貌关闭文件对象的最后一个句柄时的复制。早期版本的NTFS复制数据,而文件在刷新缓存时打开,这意味着它根据不可预测的时间表经常发生。此更改的结果是目录条目现在不经常更新,因此最后更新的文件大小已经过时了。
答案 1 :(得分:0)
您可以使用WMI为打开的文件获取正确的文件大小:
Using System.Management;
internal static ManagementObject GetFileManagementObject(string pfilepath)
{
string filepath = pfilepath.Replace("\\", "\\\\");
string scope = @"\\" + System.Environment.MachineName + @"\root\CIMV2";
string query = string.Format("Select FileSize from CIM_DataFile WHERE Name = '{0}'", filepath);
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection collection = searcher.Get();
foreach (ManagementObject mobj in searcher.Get())
{
return mobj;
}
return (null);
}
private long GetFileSizeOpenFile(string path)
{
long fsize = -1;
try
{
ManagementObject mObj = Statics.GetFileManagementObject(path);
string sSize = mObj.Properties["FileSize"].Value.ToString();
long.TryParse(sSize, out fsize);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
}
return fsize;
}