【问题标题】:Get size of file on disk获取磁盘上文件的大小
【发布时间】:2011-04-14 14:46:57
【问题描述】:
var length = new System.IO.FileInfo(path).Length;

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

我希望在 C# 中获取磁盘上文件的大小(最好没有 interop),如 Windows 资源管理器报告的那样。

它应该给出正确的尺寸,包括:

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

【问题讨论】:

    标签: c# .net filesize


    【解决方案1】:

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

    【讨论】:

    • 你确定这是正确的 if (result == 0) throw new Win32Exception(result);
    • 'if (result == 0)' 位是正确的(请参阅msdn),但你是对的,我使用了错误的构造函数。我现在就修。
    • FileInfo.Directory.Root 看起来好像不能处理任何类型的文件系统链接。所以它只适用于没有符号链接/硬链接/连接点或任何 NTFS 提供的经典本地驱动器号。
    • 谁能一步一步解释,不同步骤做了什么?了解它的实际工作原理将非常有帮助。谢谢。
    • 此代码需要命名空间System.ComponentModelSystem.Runtime.InteropServices
    【解决方案2】:

    上面的代码在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
    

    【讨论】:

    • System.Managment 引用需要此代码才能工作。除了 WMI 之外,似乎没有标准方法可以在 Windows(6.x 版本)上准确获取集群大小。 :|
    • 我在 Vista x64 机器上编写了我的代码,现在在 W7 x64 机器上以 64 位和 WOW64 模式对其进行了测试。请注意,GetDiskFreeSpace 是 supposed 以在 成功 时返回非零值。
    • 原始问题要求使用 C#
    • 这段代码甚至无法编译(使用时缺少一个右括号),并且一个衬里对于学习目的来说非常糟糕
    • 此代码在请求.First() 时也存在编译问题,因为它是IEnumerable 而不是IEnumerable&lt;T&gt;,如果您想使用该代码首先调用.Cast&lt;object&gt;()
    【解决方案3】:

    根据 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 来处理压缩和/或稀疏文件。
    【解决方案4】:

    我想会是这样的:

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

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

    【讨论】:

    • 这是文件的大小(文件中的字节数)。根据实际硬件的块大小,文件可能会占用更多磁盘空间。例如。我的 HDD 上的 600 字节文件在磁盘上使用了 4kB。所以这个答案是不正确的。
    猜你喜欢
    • 1970-01-01
    • 2020-10-17
    • 1970-01-01
    • 2018-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-07
    相关资源
    最近更新 更多