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

Ceci donne la taille logique du fichier, pas la taille sur le disque. Je souhaite obtenir la taille d'un fichier sur le disque en C# (de préférence sans interop) comme indiqué par l'Explorateur Windows.Obtenir la taille du fichier sur le disque

Il devrait donner la bonne taille, y compris pour:

  • Un fichier compressé
  • Un fichier clairsemé
  • Un fichier fragmenté

Répondre

39

Cela utilise GetCompressedFileSize, comme suggéré, ainsi que GetDiskFreeSpace, comme PaulStack suggéré, cependant, il utilise P/Invoke. Je l'ai testé uniquement pour les fichiers compressés, et je suppose que cela ne fonctionne pas pour les fichiers fragmentés.

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

êtes-vous sûr que c'est correct if (resultat == 0) throw new Win32Exception (resultat); – Simon

+0

Le bit 'if (result == 0)' est correct (voir [msdn] (http://msdn.microsoft.com/fr-fr/library/aa364935.aspx)), mais vous avez raison de dire que je suis en utilisant le mauvais constructeur. Je vais le réparer maintenant. – margnus1

+0

'FileInfo.Directory.Root' n'a pas l'air de pouvoir gérer n'importe quel type de liens de système de fichiers. Donc, il ne fonctionne que sur les lettres de lecteur locales classiques sans liens symboliques/hardlinks/points de jonction ou tout ce que NTFS a à offrir. – ygoe

4

Selon forums sociaux MSDN:

La taille sur le disque doit être la somme des taille des clusters qui stockent le fichier:
http://social.msdn.microsoft.com/Forums/en-US/Vsexpressvcs/thread/85bf76ac-a254-41d4-a3d7-e7803c8d9bc3
Vous aurez besoin de plonger dans P/Invoke pour trouver la taille de la grappe; GetDiskFreeSpace() le renvoie.

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

Mais veuillez noter que cela ne fonctionnera pas dans NTFS où la compression est activée.

+1

Je suggère d'utiliser quelque chose comme 'GetCompressedFileSize' plutôt que' filelength' pour prendre en compte les fichiers compressés et/ou fragmentés. –

14

Le code précédent ne fonctionne pas correctement sur Windows Server 2008 ou 2008 R2 ou Windows   7 et Windows   systèmes basés sur Vista que la taille de cluster est toujours égale à zéro (GetDiskFreeSpaceW et GetDiskFreeSpace retour -1 même avec UAC désactivé.) Voici le code modifié qui fonctionne.

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

Version Crack (fonctionnement complet)? – Norbert

+0

Ouais .. Lolz ... –

+0

La référence System.Managment est requise pour que ce code fonctionne. Il semble qu'il n'y ait pas de moyen standard d'obtenir une taille de cluster précise sur Windows (versions 6.x) sauf WMI. : | –

0

Je pense que ce sera comme ceci:

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

que je fais encore quelques tests pour cette , pour avoir une confirmation

+0

Cela fonctionne très bien pour moi. Merci beaucoup! –

+3

Il s'agit de la taille du fichier (nombre d'octets dans le fichier). En fonction des tailles de blocs du matériel réel, un fichier peut consommer plus d'espace disque. Par exemple. un fichier de 600 octets sur mon disque dur utilisé 4 Ko sur le disque. Donc, cette réponse est incorrecte. – 0xBADF00D