获取磁盘上文件的大小


85
var length = new System.IO.FileInfo(path).Length;

这给出了文件的逻辑大小,而不是磁盘上的大小。

我希望获得Windows资源管理器报告的C#磁盘上文件的大小(最好没有interop)。

它应该给出正确的大小,包括:

  • 压缩文件
  • 稀疏文件
  • 碎片文件

Answers:


50

它使用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);

如果(result == 0)抛出新的Win32Exception(result),您确定这是正确的吗?
西蒙(Simon)

'if(result == 0)'位是正确的(请参阅msdn),但是您使用的是错误的构造函数是正确的。我现在将修复它。
margnus1 2011年

FileInfo.Directory.Root看起来好像不能处理任何类型的文件系统链接。因此,它仅适用于没有符号链接/硬链接/连接点或任何NTFS提供的经典本地驱动器号。
ygoe 2013年

谁能逐步解释一下,在不同的步骤做了什么?理解它的实际工作方式将非常有帮助。
bapi

5
此代码需要命名空间System.ComponentModelSystem.Runtime.InteropServices
肯尼·埃维特

17

上面的代码在Windows Server 2008或2008 R2或基于Windows 7和Windows Vista的系统上无法正常工作,因为群集大小始终为零(即使禁用了UAC, GetDiskFreeSpaceW和GetDiskFreeSpace也会返回-1 。)这是经过修改的代码。

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

要使此代码正常工作,需要System.Managment参考。除了WMI之外,似乎没有其他标准方法可以在Windows(6.x版本)上准确获取群集大小。:|
史蒂夫·约翰逊

1
我在Vista x64计算机上编写了代码,现在在W7 x64计算机上以64位和WOW64模式对其进行了测试。注意,GetDiskFreeSpace应该成功时返回非零值。
margnus1 2011年

1
原始问题要求C#
Shane Courtrille 2013年

4
这段代码甚至都没有编译(使用中缺少一个右括号),并且一个衬垫对于学习目的非常糟糕
Mickael V.

1
请求时,该代码也有一个编译问题.First(),因为它是一个IEnumerable,而不是一个IEnumerable<T>,如果你想使用的代码第一个电话.Cast<object>()
约尔HALB

5

根据MSDN社交论坛:

磁盘上的大小应该是存储文件的群集大小的总和:
long sizeondisk = clustersize * ((filelength + clustersize - 1) / clustersize);
您需要使用P / Invoke来查找群集大小;GetDiskFreeSpace()返回它。

请参阅如何在C#中获取文件在磁盘上的大小

但是请注意一点,这在启用压缩的NTFS中不起作用。


2
我建议使用类似的方法,GetCompressedFileSize而不是filelength考虑压缩和/或稀疏文件。
汉斯·奥尔森,2010年

-1

我认为它将是这样的:

double ifileLength = (finfo.Length / 1048576); //return file size in MB ....

我仍在为此做一些测试,以得到确认。


7
这是文件的大小(文件中的字节数)。根据实际硬件的块大小,文件可能会占用更多磁盘空间。例如,我的HDD上的600byte文件在磁盘上使用了4kB。所以这个答案是不正确的。
0xBADF00D
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.