不使用System.IO.FileInfo获取文件大小

本文关键字:FileInfo 获取 文件大小 IO System | 更新日期: 2023-09-27 17:54:33

是否有可能在c#中不使用System.IO.FileInfo而获得文件的大小 ?

我知道你可以得到其他的东西,如名称和扩展名分别使用Path.GetFileName(yourFilePath)Path.GetExtension(yourFilePath),但显然不是文件大小?有没有另一种方法我可以得到文件大小不使用System.IO.FileInfo ?

这样做的唯一原因是,如果我是正确的,FileInfo获取的信息比我真正需要的更多,因此,如果我唯一需要的是文件的大小,那么收集所有这些FileInfo需要更长的时间。有更快的方法吗?

不使用System.IO.FileInfo获取文件大小

我使用以下两种方法执行了一个基准测试:

    public static uint GetFileSizeA(string filename)
    {
        WIN32_FIND_DATA findData;
        FindFirstFile(filename, out findData);
        return findData.nFileSizeLow;
    }
    public static uint GetFileSizeB(string filename)
    {
        IntPtr handle = CreateFile(
            filename,
            FileAccess.Read,
            FileShare.Read,
            IntPtr.Zero,
            FileMode.Open,
            FileAttributes.ReadOnly,
            IntPtr.Zero);
        long fileSize;
        GetFileSizeEx(handle, out fileSize);
        CloseHandle(handle);
        return (uint) fileSize;
    }

在2300多个文件上运行,GetFileSizeA的运行时间为62-63ms。GetFileSizeB耗时超过18秒。

除非有人看到我做错了什么,否则我认为答案是明确的,即哪种方法更快。

是否有一种方法可以避免实际打开文件?

FileAttributes变化。只读到文件属性。Normal减少了时间,使两种方法在性能上相同。

此外,如果您跳过CloseHandle()调用,GetFileSizeEx方法将变得大约快20-30%,尽管我不知道我是否建议这样做。

我做了一个简短的测试,我发现使用FileStream只比使用Pete的GetFileSizeB平均慢1毫秒(在网络共享上花了我大约21毫秒…)。就我个人而言,我更喜欢尽可能保持在BCL限制内。

代码很简单:

using (var file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    return file.Length;
}

按照这个注释:

我有一个小的应用程序,收集大小信息,并保存到一个数组…但我经常有大约50万个文件,浏览所有这些文件需要一段时间(我用的是FileInfo)。我只是想知道是否有更快的方法…

因为你要找到这么多文件的长度,你更有可能从并行化中获益,而不是通过另一种方法获得文件大小。FileInfo类应该足够好,任何改进都可能很小。

另一方面,并行化文件大小请求有显著提高速度的潜力。(请注意,改进的程度将主要取决于您的磁盘驱动器,而不是您的处理器,因此结果可能相差很大。)

不是直接回答…因为我不确定是否有更快的方法来使用。net框架。

下面是我使用的代码:

  List<long> list = new List<long>();
  DirectoryInfo di = new DirectoryInfo("C:''Program Files");
  FileInfo[] fiArray = di.GetFiles("*", SearchOption.AllDirectories);
  foreach (FileInfo f in fiArray)
    list.Add(f.Length);

运行它,在我的"Program Files"目录上运行大约22720个文件花费了2709毫秒。无论如何,这都不是一个懒散的人。此外,当我将*.txt作为GetFiles方法的第一个参数的过滤器时,它将时间大幅减少到461ms。

这在很大程度上取决于你的硬盘有多快,但我真的不认为FileInfo会影响性能。

注意:我认为这只适用于。net 4+

如果你想在非windows主机上的。net Core或Mono运行时这样做,一个快速的解决方案:

包含monox . posix . netstandard NuGet包,然后像这样…

using Mono.Unix.Native;
private long GetFileSize(string filePath)
{
    Stat stat;
    Syscall.stat(filePath, out stat);
    return stat.st_size;
}

我已经在Linux和macOS上运行。net Core测试了这个——不确定它是否能在Windows上工作——它可能可以,因为这些是底层的POSIX系统调用(这个包是由微软维护的)。如果没有,结合其他基于P/调用的答案,以覆盖所有平台。

FileInfo.Length相比,当获得另一个进程/线程正在积极写入的文件的大小时,这给了我更可靠的结果。

你可以试试:

[DllImport("kernel32.dll")]
static extern bool GetFileSizeEx(IntPtr hFile, out long lpFileSize);

但这并不是很大的进步…

下面是取自pinvoke.net的示例代码:

IntPtr handle = CreateFile(
    PathString, 
    GENERIC_READ, 
    FILE_SHARE_READ, 
    0, 
    OPEN_EXISTING, 
    FILE_ATTRIBUTE_READONLY, 
    0); //PInvoked too
if (handle.ToInt32() == -1) 
{
    return; 
}
long fileSize;
bool result = GetFileSizeEx(handle, out fileSize);
if (!result) 
{
    return;
}