2010-09-20 80 views
73
var length = new System.IO.FileInfo(path).Length; 

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

我希望得到C#中磁盘上的文件大小(最好不包括interop),正如Windows资源管理器报告的那样。

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

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

回答

39

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

你确定这是正确的吗? if(result == 0)throw new Win32Exception(result); – Simon 2011-08-30 06:21:11

+0

'if(result == 0)'位是正确的(参见[msdn](http://msdn.microsoft.com/zh-cn/library/aa364935.aspx)),但你说得对,我是使用错误的构造函数。 我现在会解决它。 – margnus1 2011-09-01 19:54:51

+0

'FileInfo.Directory。Root不看起来好像它可以处理任何类型的文件系统链接。所以它只适用于没有符号链接/硬连接/连接点或任何NTFS所提供的传统本地驱动器字母。 – ygoe 2013-08-24 18:11:28

4

根据MSDN社会论坛:

磁盘上的大小应该是存储文件的簇的大小:
http://social.msdn.microsoft.com/Forums/en-US/Vsexpressvcs/thread/85bf76ac-a254-41d4-a3d7-e7803c8d9bc3
您需要进入P/Invoke以查找簇大小; GetDiskFreeSpace()返回它。

请参阅How to get the size on disk of a file in C#

但是请注意,在打开压缩的NTFS中这不起作用。

+1

我建议使用'GetCompressedFileSize'而不是'filelength'来解释压缩和/或稀疏文件。 – 2010-09-20 11:06:28

14

上面的代码不正确的Windows Server 2008或2008 R2或Windows   7和Windows簇大小始终为零工作 基于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 
+1

破解版(全面运行)? – Norbert 2011-07-29 05:07:41

+0

是啊..洛兹... – 2011-07-29 05:12:50

+0

System.Managment引用是此代码工作所必需的。看起来似乎没有在Windows(6.x版本)上准确获取群集大小的标准方法,但WMI除外。 :| – 2011-07-29 05:14:11

0

我认为这将是这样的:

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

我仍然在做一些测试这,以获得确认。

+0

这对我来说工作得很好。非常感谢! – 2014-03-13 11:13:39

+3

这是文件的大小(文件内的字节数)。根据实际硬件的块大小,文件可能会占用更多的磁盘空间。例如。我的硬盘上有600byte的文件在磁盘上使用4kB。所以这个答案是不正确的。 – 0xBADF00D 2016-05-25 08:51:06