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

时间:2013-01-18 21:29:44

标签: c# filepath filesize fileinfo

是否可以在不使用System.IO.FileInfo的情况下在C#中获取文件的大小

我知道你可以分别使用Path.GetFileName(yourFilePath)Path.GetExtension(yourFilePath)来获取名称和扩展名等其他内容,但显然不是文件大小?还有另一种方法可以在不使用System.IO.FileInfo的情况下获得文件大小吗?

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

6 个答案:

答案 0 :(得分:8)

我使用这两种方法执行了基准测试:

    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.ReadOnly更改为FileAttributes.Normal减少了时间,以便两种方法的性能相同。

此外,如果你跳过CloseHandle()调用,GetFileSizeEx方法会快20到30%,但我不知道我会推荐它。

答案 1 :(得分:5)

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

代码很简单:

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

答案 2 :(得分:3)

根据this comment

  

我有一个小应用程序收集大小信息并将其保存到一个数组...但我经常有五十万个文件,给予或采取,需要一段时间来浏览所有这些文件(我是使用FileInfo)。我只是想知道是否有更快的方式......

由于您找到了这么多文件的长度,因此您更有可能从并行化中受益,而不是尝试通过其他方法获取文件大小。 FileInfo类应该足够好,并且任何改进都可能很小。

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

答案 3 :(得分:3)

不是一个直接的答案......因为我不确定使用.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”目录运行了2709ms,大约是22720个文件。无论如何,这都不是什么懈怠。此外,当我将*.txt作为GetFiles方法的第一个参数的过滤器时,它会将时间大幅减少到461毫秒。

这很大程度上取决于硬盘的速度,但我真的不认为FileInfo会破坏性能。

注意:我认为这仅适用于.NET 4 +

答案 4 :(得分:1)

如果要在非Windows主机上的.NET Core或Mono运行时上执行此操作,则为快速解决方案:

包含Mono.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系统调用(并且该软件包由Microsoft维护)。如果没有,请结合其他基于P / Invoke的答案以覆盖所有平台。

FileInfo.Length相比,当获取另一个进程/线程正在主动写入的文件的大小时,这给我带来了更加可靠的结果。

答案 5 :(得分:0)

你可以试试这个:

[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;
}