.net 获取磁盘上文件的大小

bzzcjhmw  于 2023-02-20  发布在  .NET
关注(0)|答案(5)|浏览(298)
var length = new System.IO.FileInfo(path).Length;

这给出了文件的逻辑大小,而不是磁盘上的大小。
我希望获得C#中磁盘上文件的大小(最好没有 interop),如Windows资源管理器所报告的。
应给予正确的尺寸,包括:

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

oo7oh9g91#

这使用了GetCompressedFileSize(如ho 1所建议的)以及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);
arknldoa

arknldoa2#

以上代码在基于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
wz8daaqr

wz8daaqr3#

根据MSDN社交论坛:
磁盘上的大小应该是存储文件的群集大小的总和:
long sizeondisk = clustersize * ((filelength + clustersize - 1) / clustersize);
您需要查看P/Invoke以找到集群大小;GetDiskFreeSpace()将返回它。
参见 * How to get the size on disk of a file in C# *。
但是请注意,这在NTFS中是不起作用的,因为NTFS中压缩是打开的。

smtd7mpg

smtd7mpg4#

如果从kernal32.dll调用外部函数GetCompressedFileSizeW,则不需要知道块大小。该函数将始终返回块大小的倍数。
唯一需要块大小的情况是,您尝试手动计算磁盘上未压缩/稀疏的文件的大小,并且您只知道文件的 * 长度 *。然后,您可以使用long sizeondisk = clustersize * ((filelength + clustersize - 1) / clustersize)手动计算磁盘上的大小(如果filelengthmodblocksize不等于0,则实质上是向上舍入到blocksize的下一个倍数)。GetCompressedFileSizeW不需要此公式,根据Microsoft文档,该函数返回用于 * 存储 * 文件的字节大小。

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));
    }
}

我已经用稀疏文件和压缩文件对此进行了测试,上面的代码总是返回与资源管理器报告的磁盘大小相同的大小。

cfh9epnr

cfh9epnr5#

我想会是这样的:

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

我还在做一些测试,以得到确认。

相关问题