【发布时间】:2011-04-14 14:46:57
【问题描述】:
var length = new System.IO.FileInfo(path).Length;
这给出了文件的逻辑大小,而不是磁盘上的大小。
我希望在 C# 中获取磁盘上文件的大小(最好没有 interop),如 Windows 资源管理器报告的那样。
它应该给出正确的尺寸,包括:
- 压缩文件
- 稀疏文件
- 碎片文件
【问题讨论】:
var length = new System.IO.FileInfo(path).Length;
这给出了文件的逻辑大小,而不是磁盘上的大小。
我希望在 C# 中获取磁盘上文件的大小(最好没有 interop),如 Windows 资源管理器报告的那样。
它应该给出正确的尺寸,包括:
【问题讨论】:
这使用 GetCompressedFileSize,正如 ho1 建议的那样,以及 GetDiskFreeSpace,如 PaulStack 建议,但它确实使用 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);
【讨论】:
FileInfo.Directory.Root 看起来好像不能处理任何类型的文件系统链接。所以它只适用于没有符号链接/硬链接/连接点或任何 NTFS 提供的经典本地驱动器号。
System.ComponentModel 和System.Runtime.InteropServices。
上面的代码在Windows Server 2008 或 2008 R2 或基于 Windows 7 和 Windows Vista 的系统上无法正常工作,因为集群大小始终为零(GetDiskFreeSpaceW 和 GetDiskFreeSpace 返回 -1,即使禁用了 UAC。)这里是修改后的有效的代码。
C#
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>()
根据 MSDN 社交论坛:
磁盘上的大小应该是存储文件的集群大小的总和:
long sizeondisk = clustersize * ((filelength + clustersize - 1) / clustersize);
您需要访问P/Invoke 来查找集群大小;GetDiskFreeSpace()退货。
见How to get the size on disk of a file in C#。
但请注意,这在打开压缩的NTFS 中不起作用。
【讨论】:
GetCompressedFileSize 而不是filelength 来处理压缩和/或稀疏文件。
我想会是这样的:
double ifileLength = (finfo.Length / 1048576); //return file size in MB ....
我仍在为此做一些测试,以获得确认。
【讨论】: