85 votes

Obtenir la taille du fichier sur le disque

var length = new System.IO.FileInfo(path).Length;

Cela donne la taille logique du fichier, et non 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 le ferait l'Explorateur Windows.

Il devrait donner la taille correcte, y compris pour :

  • Un fichier compressé
  • Un fichier épars
  • Un fichier fragmenté

50voto

margnus1 Points 684

Ceci utilise GetCompressedFileSize, comme ho1 l'a suggéré, ainsi que GetDiskFreeSpace, comme PaulStack l'a suggéré. a suggéré, il utilise cependant P/Invoke. Je ne l'ai testé que pour les fichiers compressés, et je soupçonne qu'il 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 votes

Êtes-vous sûr que c'est correct si (résultat == 0) lancez une nouvelle Win32Exception(résultat) ;

0 votes

Le bit 'if (result == 0)' est correct (voir msdn ), mais vous avez raison de dire que j'utilise le mauvais constructeur. Je vais le corriger maintenant.

0 votes

FileInfo.Directory.Root n'a pas l'air de pouvoir gérer les liens entre systèmes de fichiers. Il ne fonctionne donc que sur des lettres de lecteur locales classiques, sans lien symbolique, lien dur, point de jonction ou tout ce que NTFS a à offrir.

17voto

Steve Johnson Points 1073

Le code ci-dessus ne fonctionne pas correctement sur Windows Server 2008 ou 2008 R2 ou Windows 7 et Windows Vista car la taille du cluster est toujours nulle (GetDiskFreeSpaceW et GetDiskFreeSpace retournent -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

0 votes

La référence System.Managment est nécessaire pour que ce code fonctionne. Il semble qu'il n'y ait pas de moyen standard d'obtenir la taille d'un cluster avec précision sous Windows (versions 6.x), à part WMI. :|

1 votes

J'ai écrit mon code sur une machine Vista x64 et je l'ai maintenant testé sur une machine W7 x64 en mode 64 bits et WOW64. Notez que GetDiskFreeSpace est supposée pour retourner une valeur non nulle sur succès .

1 votes

La question originale porte sur le C#

5voto

stack72 Points 5146

Selon les forums sociaux de MSDN :

La taille sur le disque doit être la somme de la taille des clusters qui stockent le fichier :
long sizeondisk = clustersize * ((filelength + clustersize - 1) / clustersize);
Vous devrez plonger dans P/Invocation pour trouver la taille du groupe ; GetDiskFreeSpace() le renvoie.

Voir Comment obtenir la taille sur disque d'un fichier en C# ? .

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

2 votes

Je suggère d'utiliser quelque chose comme GetCompressedFileSize plutôt que filelength pour tenir compte des fichiers compressés et/ou épars.

-1voto

bapi Points 219

Je pense que ce sera comme ça :

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

Je suis encore en train de faire quelques tests pour cela, pour avoir une confirmation.

8 votes

Il s'agit de la taille du fichier (nombre d'octets dans le fichier). Selon la taille des blocs du matériel actuel, un fichier peut consommer plus d'espace disque. Par exemple, un fichier de 600 octets sur mon disque dur utilise 4 ko sur le disque. Cette réponse est donc incorrecte.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X