Récupère la taille du fichier sur le disque

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

Cela donne la taille logique du fichier, pas la taille du disque.

Je souhaite obtenir la taille d'un fichier sur le disque en C# (de préférence sans interop) comme ce serait signalé par l'Explorateur Windows.

Il devrait donner la taille correcte, y compris pour:

  • Un fichier compressé
  • un fichier clairsemé
  • Un fichier fragmenté
78
demandé sur Peter Mortensen 2010-09-20 14:37:02

4 réponses

Cela utilise GetCompressedFileSize, comme ho1 suggéré, ainsi que GetDiskFreeSpace, comme PaulStack suggéré, il utilise cependant P / Invoke. Je l'ai testé uniquement pour les fichiers compressés, et je crains 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);
41
répondu margnus1 2011-09-01 19:55:41

Le code ci-dessus ne fonctionne pas correctement sur Windows Server 2008 ou 2008 R2 ou les systèmes basés sur Windows 7 et Windows Vista car la taille du cluster est toujours nulle (GetDiskFreeSpaceW et getdiskfreespace renvoient -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
15
répondu Steve Johnson 2015-05-17 10:20:21

Selon les forums sociaux MSDN:

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

Voir Comment obtenir la taille sur le disque d'un fichier dans C#.

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

4
répondu stack72 2015-05-17 07:54:41

Je pense que ce sera comme ceci:

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

Je fais encore des tests pour cela, pour obtenir une confirmation.

0
répondu bapi 2013-11-16 18:27:29