Get file size without using System.IO.FileInfo?

前端 未结 6 1554
[愿得一人]
[愿得一人] 2021-02-13 15:51

Is it possible to get the size of a file in C# without using System.IO.FileInfo at all?

I know that you can get other things like Name and

相关标签:
6条回答
  • 2021-02-13 16:06

    From a short test i did, i've found that using a FileStream is just 1 millisecond slower in average than using Pete's GetFileSizeB (took me about 21 milliseconds over a network share...). Personally i prefer staying within the BCL limits whenever i can.

    The code is simple:

    using (var file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    {
        return file.Length;
    }
    
    0 讨论(0)
  • 2021-02-13 16:07

    A quick'n'dirty solution if you want to do this on the .NET Core or Mono runtimes on non-Windows hosts:

    Include the Mono.Posix.NETStandard NuGet package, then something like this...

    using Mono.Unix.Native;
    
    private long GetFileSize(string filePath)
    {
        Stat stat;
        Syscall.stat(filePath, out stat);
        return stat.st_size;
    }
    

    I've tested this running .NET Core on Linux and macOS - not sure if it works on Windows - it might, given that these are POSIX syscalls under the hood (and the package is maintained by Microsoft). If not, combine with the other P/Invoke-based answer to cover all platforms.

    When compared to FileInfo.Length, this gives me much more reliable results when getting the size of a file that is actively being written to by another process/thread.

    0 讨论(0)
  • 2021-02-13 16:08

    I performed a benchmark using these two methods:

        public static uint GetFileSizeA(string filename)
        {
            WIN32_FIND_DATA findData;
            FindFirstFile(filename, out findData);
            return findData.nFileSizeLow;
        }
    
        public static uint GetFileSizeB(string filename)
        {
            IntPtr handle = CreateFile(
                filename,
                FileAccess.Read,
                FileShare.Read,
                IntPtr.Zero,
                FileMode.Open,
                FileAttributes.ReadOnly,
                IntPtr.Zero);
            long fileSize;
            GetFileSizeEx(handle, out fileSize);
            CloseHandle(handle);
            return (uint) fileSize;
        }
    

    Running against a bit over 2300 files, GetFileSizeA took 62-63ms to run. GetFileSizeB took over 18 seconds.

    Unless someone sees something I'm doing wrong, I think the answer is clear as to which method is faster.

    Is there a way I can refrain from actually opening the file?

    Update

    Changing FileAttributes.ReadOnly to FileAttributes.Normal reduced the timing so that the two methods were identical in performance.

    Furthermore, if you skip the CloseHandle() call, the GetFileSizeEx method becomes about 20-30% faster, though I don't know that I'd recommend that.

    0 讨论(0)
  • 2021-02-13 16:11

    As per this comment:

    I have a small application that gathers the size info and saves it into an array... but I often have half a million files, give or take and that takes a while to go through all of those files (I'm using FileInfo). I was just wondering if there was a faster way...

    Since you're finding the length of so many files you're much more likely to benefit from parallelization than from trying to get the file size through another method. The FileInfo class should be good enough, and any improvements are likely to be small.

    Parallelizing the file size requests, on the other hand, has the potential for significant improvements in speed. (Note that the degree of improvement will be largely based on your disk drive, not your processor, so results can vary greatly.)

    0 讨论(0)
  • 2021-02-13 16:26

    Not a direct answer...because I am not sure there is a faster way using the .NET framework.

    Here's the code I am using:

      List<long> list = new List<long>();
      DirectoryInfo di = new DirectoryInfo("C:\\Program Files");
      FileInfo[] fiArray = di.GetFiles("*", SearchOption.AllDirectories);
      foreach (FileInfo f in fiArray)
        list.Add(f.Length);
    

    Running that, it took 2709ms to run on my "Program Files" directory, which was around 22720 files. That's no slouch by any means. Furthermore, when I put *.txt as a filter for the first parameter of the GetFiles method, it cut the time down drastically to 461ms.

    A lot of this will depend on how fast your hard drive is, but I really don't think that FileInfo is killing performance.

    NOTE: I thikn this only valid for .NET 4+

    0 讨论(0)
  • 2021-02-13 16:26

    You can try this:

    [DllImport("kernel32.dll")]
    static extern bool GetFileSizeEx(IntPtr hFile, out long lpFileSize);
    

    But that's not much of an improvement...

    Here's the example code taken from pinvoke.net:

    IntPtr handle = CreateFile(
        PathString, 
        GENERIC_READ, 
        FILE_SHARE_READ, 
        0, 
        OPEN_EXISTING, 
        FILE_ATTRIBUTE_READONLY, 
        0); //PInvoked too
    
    if (handle.ToInt32() == -1) 
    {
        return; 
    }
    
    long fileSize;
    bool result = GetFileSizeEx(handle, out fileSize);
    if (!result) 
    {
        return;
    }
    
    0 讨论(0)
提交回复
热议问题