Nhận kích thước của tệp trên đĩa


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

Điều này cung cấp kích thước hợp lý của tệp, không phải kích thước trên đĩa.

Tôi muốn có được kích thước của tệp trên đĩa bằng C # (tốt nhất là không có tương tác ) như Windows Explorer đã báo cáo.

Nó phải cung cấp kích thước chính xác, bao gồm:

  • Một tập tin nén
  • Một tập tin thưa thớt
  • Một tệp bị phân mảnh

Câu trả lời:


50

Điều này sử dụng GetCompressedFileSize, như ho1 đề xuất, cũng như GetDiskFreeSpace, như PaulStack đề xuất, tuy nhiên, nó sử dụng P / Invoke. Tôi đã thử nghiệm nó chỉ cho các tệp nén và tôi nghi ngờ nó không hoạt động cho các tệp bị phân mảnh.

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

bạn có chắc điều này đúng nếu (kết quả == 0) ném Win32Exception mới (kết quả);
Simon

Bit 'if (result == 0)' đúng (xem msdn ), nhưng bạn nói đúng là tôi đang sử dụng sai hàm tạo. Tôi sẽ sửa nó ngay bây giờ.
margnus1

FileInfo.Directory.Rootkhông giống như thể nó có thể xử lý bất kỳ loại liên kết hệ thống tệp nào. Vì vậy, nó chỉ hoạt động trên các ký tự ổ đĩa cục bộ cổ điển không có liên kết tượng trưng / liên kết cứng / điểm nối hoặc bất cứ điều gì NTFS cung cấp.
ygoe

Bất kỳ ai có thể vui lòng đưa ra lời giải thích từng bước, những gì đã được thực hiện ở các bước khác nhau? Nó sẽ rất hữu ích để hiểu, nó thực sự hoạt động như thế nào.
bapi

5
Mã này yêu cầu không gian tên System.ComponentModelSystem.Runtime.InteropServices.
Kenny Evitt

17

Mã trên không hoạt động bình thường trên Windows Server 2008 hoặc 2008 R2 hoặc hệ thống dựa trên Windows 7 và Windows Vista vì kích thước cụm luôn bằng 0 (GetDiskFreeSpaceW và GetDiskFreeSpace trả về -1 ngay cả khi UAC bị vô hiệu hóa.) Đây là mã đã sửa đổi hoạt động.

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

Tham chiếu System.Managment là bắt buộc để mã này hoạt động. Dường như không có cách tiêu chuẩn nào để lấy kích thước cụm chính xác trên Windows (phiên bản 6.x) ngoại trừ WMI. : |
Steve Johnson,

1
Tôi đã viết mã của mình trên máy Vista x64 và bây giờ đã thử nghiệm nó trên máy W7 x64 ở chế độ 64-bit và WOW64. Lưu ý rằng GetDiskFreeSpace phải trả về nonzero khi thành công .
margnus1

1
Original câu hỏi yêu cầu C #
Shane Courtrille

4
Mã này thậm chí không biên dịch (một đóng ngoặc mất tích trên sử dụng) và một lót là rất khủng khiếp cho các mục đích học tập
Mickael V.

1
Mã này cũng có một vấn đề biên dịch khi yêu cầu .First()vì nó là một IEnumerablevà không phải là một IEnumerable<T>, nếu bạn muốn sử dụng mã đầu tiên cuộc gọi.Cast<object>()
halb Yoel

5

Theo diễn đàn xã hội MSDN:

Kích thước trên đĩa phải là tổng kích thước của các cụm lưu trữ tệp:
long sizeondisk = clustersize * ((filelength + clustersize - 1) / clustersize);
Bạn sẽ cần phải nhúng vào P / Invoke để tìm kích thước cụm; GetDiskFreeSpace()trả lại nó.

Xem Cách lấy kích thước trên đĩa của tệp trong C # .

Nhưng xin lưu ý điểm rằng điều này sẽ không hoạt động trong NTFS khi tính năng nén được bật.


2
Tôi khuyên bạn nên sử dụng một cái gì đó giống như GetCompressedFileSizethay vì filelengthđể tính cho các tệp nén và / hoặc thưa thớt.
Hans Olsson

-1

Tôi nghĩ nó sẽ như thế này:

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

Tôi vẫn đang thực hiện một số thử nghiệm cho điều này, để có được xác nhận.


7
Đây là kích thước của tệp (số byte trong tệp). Tùy thuộc vào kích thước khối của phần cứng thực, một tệp có thể tiêu tốn nhiều dung lượng đĩa hơn. Ví dụ: một tệp 600byte trên ổ cứng HDD của tôi đã sử dụng 4kB trên đĩa. Vì vậy, câu trả lời này là không chính xác.
0xBADF00D
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.