You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#中高效批量获取文件名与文件长度?

高效获取大量文件路径与大小的解决方案

方案1:反射读取FileSystemInfo内部缓存的属性数据

DirectoryInfo.GetFileSystemInfos()返回的FileSystemInfo实例已经在内部缓存了WIN32_FILE_ATTRIBUTE_DATA结构体,包含文件大小信息,可通过反射直接读取,避免重复创建FileInfo实例带来的性能损耗:

using System.Reflection;

// 获取FileSystemInfo内部存储属性数据的字段
var attributeDataField = typeof(FileSystemInfo).GetField(
    "_win32FileAttributeData", 
    BindingFlags.NonPublic | BindingFlags.Instance
);

if (attributeDataField == null)
{
    throw new NotSupportedException("当前.NET版本不支持此反射读取方式");
}

DirectoryInfo directoryInfo = new DirectoryInfo(path);
List<MyInfo> myInfoList = directoryInfo.GetFileSystemInfos()
    .Where(f => (f.Attributes & FileAttributes.Directory) != FileAttributes.Directory)
    .Select(f => 
    {
        var attributeData = attributeDataField.GetValue(f);
        // 读取结构体中的文件大小字段
        var fileSizeHigh = (uint)attributeData.GetType()
            .GetField("dwFileSizeHigh", BindingFlags.Public | BindingFlags.Instance)!
            .GetValue(attributeData);
        var fileSizeLow = (uint)attributeData.GetType()
            .GetField("dwFileSizeLow", BindingFlags.Public | BindingFlags.Instance)!
            .GetValue(attributeData);
        long fileSize = (long)fileSizeHigh << 32 | fileSizeLow;

        return new MyInfo
        {
            FilePath = f.FullName,
            FileSize = fileSize
        };
    })
    .ToList();

注意:该方法依赖.NET内部实现细节,不同版本(如.NET Framework与.NET Core/.NET 5+)可能存在字段名或结构体定义差异,需自行测试适配。

方案2:直接调用Win32 API扫描文件

这是性能最优的方案,通过FindFirstFile/FindNextFile底层API一次性获取所有文件的路径与大小,完全避免.NET类库的额外IO开销:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

public static class FileScanner
{
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    private struct WIN32_FIND_DATA
    {
        public FileAttributes dwFileAttributes;
        public System.Runtime.InteropServices.ComTypes.FILETIME ftCreationTime;
        public System.Runtime.InteropServices.ComTypes.FILETIME ftLastAccessTime;
        public System.Runtime.InteropServices.ComTypes.FILETIME ftLastWriteTime;
        public uint nFileSizeHigh;
        public uint nFileSizeLow;
        public uint dwReserved0;
        public uint dwReserved1;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
        public string cFileName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 14)]
        public string cAlternateFileName;
    }

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern IntPtr FindFirstFile(string lpFileName, out WIN32_FIND_DATA lpFindFileData);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
    private static extern bool FindNextFile(IntPtr hFindFile, out WIN32_FIND_DATA lpFindFileData);

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool FindClose(IntPtr hFindFile);

    public static List<MyInfo> ScanFiles(string path)
    {
        var fileList = new List<MyInfo>();
        string searchPattern = System.IO.Path.Combine(path, "*");

        IntPtr findHandle = FindFirstFile(searchPattern, out WIN32_FIND_DATA fileData);
        if (findHandle == IntPtr.Zero)
        {
            return fileList;
        }

        try
        {
            do
            {
                // 跳过目录项
                if ((fileData.dwFileAttributes & FileAttributes.Directory) != 0)
                {
                    continue;
                }

                // 计算文件总大小
                long fileSize = (long)fileData.nFileSizeHigh << 32 | fileData.nFileSizeLow;
                string fullPath = System.IO.Path.Combine(path, fileData.cFileName);

                fileList.Add(new MyInfo
                {
                    FilePath = fullPath,
                    FileSize = fileSize
                });

            } while (FindNextFile(findHandle, out fileData));
        }
        finally
        {
            FindClose(findHandle);
        }

        return fileList;
    }
}

// 调用示例
List<MyInfo> myInfoList = FileScanner.ScanFiles(path);

该方案不依赖.NET内部实现,稳定性与性能都更适合处理数万个文件的场景。

方案3:利用.NET Core 3.0+的FileSystemInfo优化

在较新的.NET版本中,DirectoryInfo.EnumerateFileSystemInfos()返回的文件实例本质是FileInfo,其Length属性已缓存扫描时获取的数据,无需额外IO操作:

DirectoryInfo directoryInfo = new DirectoryInfo(path);
List<MyInfo> myInfoList = directoryInfo.EnumerateFileSystemInfos()
    .OfType<FileInfo>()
    .Select(f => new MyInfo
    {
        FilePath = f.FullName,
        FileSize = f.Length
    })
    .ToList();

这里用OfType<FileInfo>直接过滤文件,比通过属性判断更简洁高效,且f.Length不会触发额外的文件系统调用。

内容的提问来源于stack exchange,提问作者T N

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 05:45:01