var length = new System.IO.FileInfo(path).Length;
这提供了文件的逻辑大小,而不是磁盘上的大小。
我希望在C#中获取文件在磁盘上的大小(最好不使用interop),就像Windows资源管理器报告的那样。
它应该能正确地给出包括以下情况的大小:
- 压缩文件
- 稀疏文件
- 分段文件
按照ho1的建议,此代码使用GetCompressedFileSize和PaulStack所建议的GetDiskFreeSpace,但它需要使用P/Invoke。我只对压缩文件进行了测试,我怀疑它无法用于碎片文件。
public static long GetFileSizeOnDisk(string file)
{
FileInfo info = new FileInfo(file);
uint dummy, sectorsPerCluster, bytesPerSector;
int result = GetDiskFreeSpaceW(info.Directory.Root.FullName, out sectorsPerCluster, out bytesPerSector, out dummy, out dummy);
if (result == 0) throw new Win32Exception();
uint clusterSize = sectorsPerCluster * bytesPerSector;
uint hosize;
uint losize = GetCompressedFileSizeW(file, out hosize);
long size;
size = (long)hosize << 32 | losize;
return ((size + clusterSize - 1) / clusterSize) * clusterSize;
}
[DllImport("kernel32.dll")]
static extern uint GetCompressedFileSizeW([In, MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
[Out, MarshalAs(UnmanagedType.U4)] out uint lpFileSizeHigh);
[DllImport("kernel32.dll", SetLastError = true, PreserveSig = true)]
static extern int GetDiskFreeSpaceW([In, MarshalAs(UnmanagedType.LPWStr)] string lpRootPathName,
out uint lpSectorsPerCluster, out uint lpBytesPerSector, out uint lpNumberOfFreeClusters,
out uint lpTotalNumberOfClusters);
public static long GetFileSizeOnDisk(string file)
{
FileInfo info = new FileInfo(file);
uint clusterSize;
using(var searcher = new ManagementObjectSearcher("select BlockSize,NumberOfBlocks from Win32_Volume WHERE DriveLetter = '" + info.Directory.Root.FullName.TrimEnd('\\') + "'") {
clusterSize = (uint)(((ManagementObject)(searcher.Get().First()))["BlockSize"]);
}
uint hosize;
uint losize = GetCompressedFileSizeW(file, out hosize);
long size;
size = (long)hosize << 32 | losize;
return ((size + clusterSize - 1) / clusterSize) * clusterSize;
}
[DllImport("kernel32.dll")]
static extern uint GetCompressedFileSizeW(
[In, MarshalAs(UnmanagedType.LPWStr)] string lpFileName,
[Out, MarshalAs(UnmanagedType.U4)] out uint lpFileSizeHigh);
VB.NET
Private Function GetFileSizeOnDisk(file As String) As Decimal
Dim info As New FileInfo(file)
Dim blockSize As UInt64 = 0
Dim clusterSize As UInteger
Dim searcher As New ManagementObjectSearcher( _
"select BlockSize,NumberOfBlocks from Win32_Volume WHERE DriveLetter = '" + _
info.Directory.Root.FullName.TrimEnd("\") + _
"'")
For Each vi As ManagementObject In searcher.[Get]()
blockSize = vi("BlockSize")
Exit For
Next
searcher.Dispose()
clusterSize = blockSize
Dim hosize As UInteger
Dim losize As UInteger = GetCompressedFileSizeW(file, hosize)
Dim size As Long
size = CLng(hosize) << 32 Or losize
Dim bytes As Decimal = ((size + clusterSize - 1) / clusterSize) * clusterSize
Return CDec(bytes) / 1024
End Function
<DllImport("kernel32.dll")> _
Private Shared Function GetCompressedFileSizeW( _
<[In](), MarshalAs(UnmanagedType.LPWStr)> lpFileName As String, _
<Out(), MarshalAs(UnmanagedType.U4)> lpFileSizeHigh As UInteger) _
As UInteger
End Function
.First()
时,此代码也存在编译问题,因为它是IEnumerable
而不是IEnumerable<T>
。如果要使用该代码,请先调用.Cast<object>()
。 - yoel halblong sizeondisk = clustersize * ((filelength + clustersize - 1) / clustersize);
GetDiskFreeSpace()
返回它。GetCompressedFileSize
而不是filelength
来处理压缩和/或稀疏文件。 - Hans Olsson如果您从kernal32.dll调用外部函数GetCompressedFileSizeW,则不需要知道块大小。该函数将始终返回块大小的倍数。
唯一需要块大小的情况是,如果您试图手动计算未压缩/稀疏的文件在磁盘上的大小,并且仅知道文件的长度。然后,您可以使用long sizeondisk = clustersize * ((filelength + clustersize - 1) / clustersize)
手动计算磁盘上的大小(如果filelength mod blocksize不等于0,则实质上将其四舍五入到下一个块大小的倍数)。根据Microsoft文档,此公式不适用于GetCompressedFileSizeW,该函数返回用于存储文件的字节大小。
using System.Runtime.InteropServices;
class GetSize
{
public static ulong GetFileSizeOnDisk(string file)
{
uint low_bytes = GetCompressedFileSizeW(file, out uint high_bytes);
ulong size = ((ulong)high_bytes << 32) | low_bytes;
return size;
}
[DllImport("kernel32.dll")]
static extern uint GetCompressedFileSizeW([In, MarshalAs(UnmanagedType.LPWStr)] string lpFileName, [Out, MarshalAs(UnmanagedType.U4)] out uint lpFileSizeHigh);
public static void Main()
{
string file = @"C:\yourfile.txt";
Console.WriteLine(GetFileSizeOnDisk(file));
}
}
我已经测试了稀疏文件和压缩文件,并且上述代码始终返回资源管理器报告的磁盘上的相同大小。
我认为它会是这样的:
double ifileLength = (finfo.Length / 1048576); //return file size in MB ....
我还在进行一些测试,以获得确认。
FileInfo.Directory.Root
看起来似乎无法处理任何类型的文件系统链接。因此,它仅适用于经典本地驱动器字母,没有符号链接/硬链接/连接点或 NTFS 提供的任何其他内容。 - ygoeSystem.ComponentModel
和System.Runtime.InteropServices
。 - Kenny Evittuser\appdata\local\microsoft\windowsapps\Microsoft.DesktopAppInstaller_somekey\...
文件夹中发现了一个奇怪的 bug。所有文件在 Windows 资源管理器中都显示为 0 磁盘大小,但是使用这个方法却显示了一个很大的大小。 - Daniel Möller