.NET FileInfo.LastWriteTime和FileInfo.LastAccessTime是错误的。

28

当我调用FileInfo(path).LastAccessTime或者FileInfo(path).LastWriteTime来获取正在被写入的文件的信息时,它返回的是文件创建时间,而不是最后一次写入的时间(即当前时间)。

有没有办法获取到这个信息?

编辑:对于目前所有的回复,我都没有尝试过使用Refresh(),但这也行不通。无论是静态方法还是创建一个新的FileInfo实例,返回的都是文件开始被写入的时间。

Codymanix可能有答案,但我没有运行Windows Server(使用的是Windows 7),也不知道在哪里测试设置。

编辑2:没人觉得这个函数似乎不起作用吗?


我也发现这些函数提供的信息不可靠,例如当文件位于(samba)网络共享上时。我注意到MSDN文档(http://msdn.microsoft.com/en-us/library/system.io.file.getlastaccesstime(v=VS.90).aspx)现在说:“此方法可能返回不准确的值...” - Booji Boy
有趣的是,这个函数不起作用 - 在Windows上似乎不可能获取文件的最后修改日期。似乎也没有可用的解决方法。如果您的应用程序想要绝对知道文件是否已更改,则必须轮询文件并哈希其全部内容。 - speciesUnknown
我在Linux上运行.NET 6应用程序时遇到了这个问题。我想知道一个文件是否被修改过。经过一些测试,我确保使用该文件的进程已被处理并且在刷新fileinfo之前等待5秒钟。虽然不够优雅,但似乎可以工作。 - Wolfgang Jacques
7个回答

16
< p> FileInfo 值仅被加载一次,然后被缓存。要获取当前值,在获取属性之前调用 Refresh()

f.Refresh();
t = f.LastAccessTime;

另一种获取当前值的方法是使用File类上的静态方法:
t = File.GetLastAccessTime(path);

3
如果进程保存但锁定文件,那么t = File.GetLastAccessTime(path);仍然过时。这个答案是不正确的。 - rollsch

9

5

正如James所指出的那样,LastAccessTime没有被更新


自Vista以来,LastWriteTime也经历了一些变化。当一个进程仍然打开文件并且另一个进程检查LastWriteTime时,它将长时间看不到新的写入时间,直到该进程关闭文件。

解决方法是您可以从外部进程中打开和关闭文件。在这样做后,您可以尝试再次读取LastWriteTime,这是最新的值。


文件系统通道:

如果一个应用程序实现了类似于滚动记录器的东西,它会关闭文件,然后将其重命名为不同的文件名,那么您也会遇到问题,因为旧文件的创建时间和文件大小由操作系统记住,即使您重新创建了log.txt,它的大小仍然为0字节。这个特性称为操作系统文件系统通道,在Windows 8.1上仍然存在。有关解决此问题的示例,请查看Enterprise Library中的RollingFlatFileTracelistener

您可以从cmd shell在自己的计算机上看到文件系统通道的效果。

echo test > file1.txt
ren file1.txt file2.txt
Wait one minute
echo test > file1.txt

dir  /tc file*.txt
...
05.07.2015  19:26                 7 file1.txt
05.07.2015  19:26                 7 file2.txt

文件系统是一个状态机。如果您关注性能和正确性,保持状态正确同步是很难的。
这种奇怪的隧道症候群显然仍然被一些应用程序使用,例如自动保存文件并将其移动到保存位置,然后重新在同一位置创建文件。对于这些应用程序来说,给文件一个新的创建日期是没有意义的,因为它只是被复制了。一些安装程序也会使用这样的技巧,将文件暂时移动到不同的位置,并稍后将内容写回以通过某些安装挂钩的某些文件存在检查。

4

来自MSDN:

FileSystemInfo在第一次调用时会调用Refresh并返回API获取属性等信息的缓存信息。在后续调用中,您必须调用Refresh以获得最新的信息副本。

FileSystemInfo.Refresh()

如果您的应用程序是写入文件的那个,我认为您需要在每个数据缓冲区写入之间自己设置LastWriteTime属性来“触摸”文件。下面是一些伪代码:

while(bytesWritten < totalBytes)
{
   bytesWritten += br.Write(buffer);
   myFileInfo.LastWriteTime = DateTime.Now;
}

我不确定这会对写入性能产生多大影响。


4
你尝试过在访问属性之前调用Refresh()(以避免获取缓存值)吗?如果这样做不起作用,你是否查看了同时出现在资源管理器中的信息?如果资源管理器显示的信息有误,那么很可能是你无法真正解决的问题——例如,可能只有在文件句柄关闭时才会更新信息。

4

在Windows系统中,有一项设置通常会在服务器系统上进行设置,以便不设置文件的修改和访问时间,以提高性能。


codymanix是正确的,他们在这里谈论了这个设置:http://blogs.technet.com/b/filecab/archive/2006/11/07/disabling-last-access-time-in-windows-vista-to-improve-ntfs-performance.aspx - DukeOfMarmalade
那个链接仅提到了最后修改时间。 - Roger Lipscombe
@RogerLipscombe它说:“我们已禁用对最后访问时间的更新”。它确实简要提到了最后修改时间,但似乎并没有说它不再更新。 - StayOnTarget

0
Tommy Carlier的回答让我开始思考...
一个好的方法是分别运行这两个片段(我只是使用了LinqPAD),同时运行sysinternals Process Monitor,以便更好地理解它们之间的差异。
while(true)
    File.GetLastAccessTime([file path here]);

并且

FileInfo bob = new FileInfo(path);

while(true){
    string accessed = bob.LastAccessTime.ToString();
}

如果您在运行第一个代码片段时查看进程监视器,您将看到对LinqPAD进程文件的重复和持续访问尝试。第二个代码片段将进行文件的初始访问,您将在进程监视器中看到活动,然后很少再次出现。

但是,如果您去修改文件(我只是使用FileInfo打开了我正在监视的文本文件并添加了一个字符并保存),则会看到LinqPAD进程对文件进行一系列访问尝试。

这说明了两种不同方法的非缓存和缓存行为。

非缓存方法会在硬盘上磨损吗?!

编辑

我感到自己在测试方面非常聪明,然后在我的Windows服务中使用了FileInfo的缓存行为(基本上是在循环中坐着并说“文件已更改-文件已更改...”然后进行处理)

虽然这种方法在我的开发框中有效,但在生产环境中却无效,即使文件已更改,该进程仍将继续运行。最终,我改变了我的方法来检查并只使用GetLastAccessTime作为其中的一部分。不知道为什么它在生产服务器上的行为会有所不同....但我目前并不太担心。


最终是的,时间和空间也是如此。 - PsyKzz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接