รับขนาดไฟล์บนดิสก์


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

สิ่งนี้ให้ขนาดโลจิคัลของไฟล์ไม่ใช่ขนาดบนดิสก์

ฉันหวังว่าจะได้รับขนาดของไฟล์บนดิสก์ใน C # (โดยเฉพาะโดยไม่ต้องทำงานร่วมกัน ) ขณะที่จะได้รับการรายงานจาก Windows Explorer

ควรให้ขนาดที่ถูกต้องรวมทั้งสำหรับ:

  • ไฟล์บีบอัด
  • ไฟล์กระจัดกระจาย
  • ไฟล์ที่แยกส่วน

คำตอบ:


50

สิ่งนี้ใช้ GetCompressedFileSize ตามที่ ho1 แนะนำเช่นเดียวกับ GetDiskFreeSpace ตามที่ PaulStack แนะนำอย่างไรก็ตามใช้ P / Invoke ฉันได้ทดสอบมันสำหรับไฟล์บีบอัดเท่านั้นและฉันสงสัยว่ามันใช้ไม่ได้กับไฟล์ที่แยกส่วน

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) โยน Win32Exception ใหม่ (ผลลัพธ์);
Simon

บิต 'if (result == 0)' ถูกต้อง (ดูmsdn ) แต่คุณคิดถูกที่ฉันใช้ตัวสร้างผิด ฉันจะแก้ไขเดี๋ยวนี้
margnus1

FileInfo.Directory.Rootไม่ได้ดูราวกับว่ามันสามารถจัดการลิงค์ระบบไฟล์ประเภทใดก็ได้ ดังนั้นจึงใช้งานได้เฉพาะกับอักษรระบุไดรฟ์ในเครื่องแบบคลาสสิกที่ไม่มี symlinks / hardlinks / จุดเชื่อมต่อหรืออะไรก็ตามที่ NTFS มีให้
ygoe

มีใครช่วยอธิบายทีละขั้นตอนได้ไหมว่ามีขั้นตอนต่างกันอย่างไร จะเป็นประโยชน์มากที่จะเข้าใจว่ามันใช้งานได้จริงอย่างไรขอบคุณ
bapi

5
รหัสนี้ต้องการเนมสเปซSystem.ComponentModelและSystem.Runtime.InteropServices.
Kenny Evitt

17

โค้ดด้านบนทำงานไม่ถูกต้องบนWindows Server 2008หรือ 2008 R2 หรือระบบที่ใช้ Windows 7 และ Windows Vista เนื่องจากขนาดคลัสเตอร์เป็นศูนย์เสมอ (GetDiskFreeSpaceW และ GetDiskFreeSpace return -1 ถึงแม้จะปิดใช้งานUACแล้วก็ตาม) นี่คือโค้ดที่แก้ไขที่ใช้งานได้

ค#

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

จำเป็นต้องมีการอ้างอิง System.Managment เพื่อให้รหัสนี้ทำงานได้ ดูเหมือนว่าไม่มีวิธีมาตรฐานในการรับขนาดคลัสเตอร์อย่างถูกต้องบน Windows (เวอร์ชัน 6.x) ยกเว้น WMI : |
Steve Johnson

1
ฉันเขียนรหัสของฉันบนเครื่อง Vista x64 และตอนนี้ได้ทดสอบบนเครื่อง W7 x64 ในโหมด 64 บิตและ WOW64 โปรดทราบว่า GetDiskFreeSpace จะควรที่จะกลับมาเป็นศูนย์ในการประสบความสำเร็จ
margnus1

1
คำถามเดิมถาม C #
Shane Courtrille

4
โค้ดนี้ไม่ได้คอมไพล์ด้วยซ้ำ (วงเล็บปิดหนึ่งอันขาดไปในการใช้งาน) และซับอันเดียวแย่มากสำหรับจุดประสงค์ในการเรียนรู้
ลโวลต์

1
รหัสนี้ยังมีปัญหาในการคอมไพล์เมื่อขอ.First()เนื่องจากเป็นIEnumerableและไม่ใช่IEnumerable<T>หากคุณต้องการใช้รหัสครั้งแรก.Cast<object>()
yoel halb

5

ตามฟอรัมโซเชียล MSDN:

ขนาดบนดิสก์ควรเป็นผลรวมของขนาดของคลัสเตอร์ที่เก็บไฟล์:
long sizeondisk = clustersize * ((filelength + clustersize - 1) / clustersize);
คุณจะต้องจิ้มลงในP / Invokeเพื่อค้นหาขนาดคลัสเตอร์ GetDiskFreeSpace()ส่งคืน

ดูวิธีการได้รับขนาดบนดิสก์ของไฟล์ใน C #

แต่โปรดทราบว่าสิ่งนี้จะไม่ทำงานในNTFSเมื่อเปิดการบีบอัด


2
ฉันขอแนะนำให้ใช้สิ่งที่ต้องการGetCompressedFileSizeแทนที่จะfilelengthพิจารณาไฟล์ที่บีบอัดและ / หรือกระจัดกระจาย
Hans Olsson

-1

ฉันคิดว่ามันจะเป็นแบบนี้:

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

ฉันยังคงทำการทดสอบบางอย่างเพื่อรับคำยืนยัน


7
นี่คือขนาดไฟล์ (จำนวนไบต์ภายในไฟล์) ขึ้นอยู่กับขนาดบล็อกของฮาร์ดแวร์จริงไฟล์อาจใช้พื้นที่ดิสก์มากขึ้น เช่นไฟล์ 600byte บน HDD ของฉันที่ใช้ 4kB บนดิสก์ ดังนั้นคำตอบนี้ไม่ถูกต้อง
0xBADF00D
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.