Holen Sie sich die Größe der Datei auf der Festplatte


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

Dies gibt die logische Größe der Datei an, nicht die Größe auf der Festplatte.

Ich möchte die Größe einer Datei auf der Festplatte in C # (vorzugsweise ohne Interop ) erhalten, wie dies vom Windows Explorer gemeldet wird.

Es sollte die richtige Größe geben, einschließlich für:

  • Eine komprimierte Datei
  • Eine spärliche Datei
  • Eine fragmentierte Datei

Antworten:


50

Dies verwendet GetCompressedFileSize, wie von ho1 vorgeschlagen, sowie GetDiskFreeSpace, wie von PaulStack vorgeschlagen, verwendet jedoch P / Invoke. Ich habe es nur für komprimierte Dateien getestet und ich vermute, dass es für fragmentierte Dateien nicht funktioniert.

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);

Sind Sie sicher, dass dies korrekt ist, wenn (Ergebnis == 0) eine neue Win32Exception (Ergebnis) auslöst?
— Simon

Das Bit 'if (result == 0)' ist korrekt (siehe msdn ), aber Sie haben Recht, dass ich den falschen Konstruktor verwende. Ich werde es jetzt beheben.
— März

FileInfo.Directory.Rootsieht nicht so aus, als könnte es irgendeine Art von Dateisystem-Links verarbeiten. Es funktioniert also nur mit klassischen lokalen Laufwerksbuchstaben ohne Symlinks / Hardlinks / Junction Points oder was auch immer NTFS zu bieten hat.
— Ygoe

Könnte jemand bitte Schritt für Schritt erklären, was in verschiedenen Schritten getan wurde? Es wird sehr hilfreich sein zu verstehen, wie es tatsächlich funktioniert. Danke.
— Bapi

5
Dieser Code erfordert die Namespaces System.ComponentModelund System.Runtime.InteropServices.
— Kenny Evitt

17

Der obige Code nicht richtig funktioniert Windows Server 2008 oder 2008 R2 oder Windows 7 und Windows Vista - basierte Systeme wie Clustergröße immer Null (GetDiskFreeSpaceW und GetDiskFreeSpace return -1 sogar mit UAC deaktiviert.) Hier ist der modifizierte Code , das funktioniert.

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

Die System.Managment-Referenz ist erforderlich, damit dieser Code funktioniert. Es scheint, als gäbe es außer WMI keine Standardmethode, um die Clustergröße unter Windows (6.x-Versionen) genau zu ermitteln. : |
— Steve Johnson

1
Ich habe meinen Code auf einem Vista x64-Computer geschrieben und ihn jetzt auf einem W7 x64-Computer im 64-Bit- und WOW64-Modus getestet. Beachten Sie, dass GetDiskFreeSpace bei Erfolg einen Wert ungleich Null zurückgeben soll .
— März

1
Ursprüngliche Frage fragt nach C #
— Shane Courtrille

4
Dieser Code wird nicht einmal kompiliert (eine schließende Klammer fehlt bei der Verwendung) und der eine Liner ist für Lernzwecke sehr schrecklich
— Mickael V.

1
Dieser Code hat auch ein Kompilierungsproblem bei der Anforderung, .First()da es sich um einen IEnumerableund keinen handelt IEnumerable<T>, wenn Sie den Code zum ersten Mal verwenden möchten.Cast<object>()
— yoel halb

4

Laut MSDN-Sozialforen:

Die Größe auf der Festplatte sollte die Summe der Größe der Cluster sein, in denen die Datei gespeichert ist:
long sizeondisk = clustersize * ((filelength + clustersize - 1) / clustersize);
Sie müssen in P / Invoke eintauchen , um die Clustergröße zu ermitteln. GetDiskFreeSpace()gibt es zurück.

Siehe So ermitteln Sie die Größe einer Datei in C # auf der Festplatte .

Beachten Sie jedoch, dass dies in NTFS, in dem die Komprimierung aktiviert ist, nicht funktioniert .


2
Ich schlage vor, etwas zu verwenden, GetCompressedFileSizeanstatt filelengthkomprimierte und / oder spärliche Dateien zu berücksichtigen.
— Hans Olsson

-1

Ich denke, es wird so sein:

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

Ich mache noch einige Tests dafür, um eine Bestätigung zu erhalten.


7
Dies ist die Größe der Datei (Anzahl der Bytes in der Datei). Abhängig von der Blockgröße der tatsächlichen Hardware belegt eine Datei möglicherweise mehr Speicherplatz. Beispielsweise hat eine 600-Byte-Datei auf meiner Festplatte 4 KB auf der Festplatte verwendet. Diese Antwort ist also falsch.
— 0xBADF00D
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.