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
Wernight
quelle

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);
margnus1
quelle
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
Steve Johnson
quelle
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 .

stack72
quelle
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.

Bapi
quelle
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