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

Esto proporciona el tamaño lógico del archivo, no el tamaño en el disco.Obtener el tamaño del archivo en el disco

Deseo obtener el tamaño de un archivo en el disco en C# (preferiblemente sin interopera) como lo informaría el Explorador de Windows.

Debe facilitar al tamaño correcto, incluyendo para:

  • un archivo comprimido
  • un archivo disperso
  • un archivo fragmentado

Respuesta

39

Esto usa GetCompressedFileSize, como se sugirió ho1, así como GetDiskFreeSpace, como sugería PaulStack ; sin embargo, usa P/Invoke. Lo he probado solo para archivos comprimidos, y sospecho que no funciona para archivos fragmentados.

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

¿está seguro de que es correcto si (resultado == 0) lanza una nueva Win32Exception (resultado); – Simon

+0

El bit 'if (result == 0)' es correcto (ver [msdn] (http://msdn.microsoft.com/en-us/library/aa364935.aspx)), pero tienes razón de que yo soy usando el constructor equivocado. Lo arreglaré ahora. – margnus1

+0

'FileInfo.Directory.Root' no parece que pueda manejar ningún tipo de enlaces de sistema de archivos. Por lo tanto, solo funciona en letras de unidad locales clásicas sin enlaces simbólicos/enlaces permanentes/puntos de unión o lo que NTFS tiene para ofrecer. – ygoe

4

Según MSDN sociales:

El tamaño en el disco debe ser la suma de tamaño de los clústeres que almacenan el archivo:
http://social.msdn.microsoft.com/Forums/en-US/Vsexpressvcs/thread/85bf76ac-a254-41d4-a3d7-e7803c8d9bc3
Necesitará buscar en P/Invoke para encontrar el tamaño del clúster; GetDiskFreeSpace() lo devuelve.

Ver How to get the size on disk of a file in C#.

Pero tenga en cuenta que esto no funcionará en NTFS donde la compresión está activada.

+1

Sugiero usar algo como 'GetCompressedFileSize' en lugar de' filelength' para dar cuenta de los archivos comprimidos y/o dispersos. –

14

El código anterior no funciona correctamente en Windows Server 2008 o 2008 R2 o Windows 7 y Windows     sistemas basados ​​en Vista como el tamaño del clúster es siempre cero (GetDiskFreeSpaceW y GetDiskFreeSpace devuelven -1 incluso con UAC desactivado.) Aquí está el código modificado que funciona.

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

Versión crackeada (en pleno funcionamiento)? – Norbert

+0

Sí ... Lolz ... –

+0

Sistema. Se requiere una referencia de proceso para que funcione este código. Parece que no hay una forma estándar de obtener el tamaño del clúster de forma precisa en Windows (versiones 6.x), excepto WMI. : | –

0

Creo que va a ser así:

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

Todavía estoy haciendo algunas pruebas para este , para obtener una confirmación.

+0

Esto funciona bien para mí. ¡Muchas gracias! –

+3

Este es el tamaño del archivo (número de bytes dentro del archivo). Dependiendo de los tamaños de bloque del hardware real, un archivo puede consumir más espacio en disco. P.ej. un archivo de 600bytes en mi HDD usó 4kB en el disco. Entonces esta respuesta es incorrecta. – 0xBADF00D

Cuestiones relacionadas