如何在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
相关产品推荐
相关产品推荐

