在C#中将大文件读入字节数组的最佳方法是什么?

Ton*_*ich 377 .net c# bytearray binary-data

我有一个Web服务器,它将大型二进制文件(几兆字节)读入字节数组.服务器可能同时读取多个文件(不同的页面请求),所以我正在寻找最优化的方法来做到这一点,而不会对CPU造成过多的负担.下面的代码是否足够好?

public byte[] FileToByteArray(string fileName)
{
    byte[] buff = null;
    FileStream fs = new FileStream(fileName, 
                                   FileMode.Open, 
                                   FileAccess.Read);
    BinaryReader br = new BinaryReader(fs);
    long numBytes = new FileInfo(fileName).Length;
    buff = br.ReadBytes((int) numBytes);
    return buff;
}
Run Code Online (Sandbox Code Playgroud)

Meh*_*ari 754

只需用以下内容替换整个内容:

return File.ReadAllBytes(fileName);
Run Code Online (Sandbox Code Playgroud)

但是,如果您担心内存消耗,则不应该一次性将所有文件都读入内存.你应该以大块的方式做到这一点.

  • 此方法仅限于2 ^ 32字节文件(4.2 GB) (35认同)
  • File.ReadAllBytes使用大文件抛出OutOfMemoryException(使用630 MB文件测试但失败) (10认同)
  • @ juanjo.arana是的,嗯......当然总有一些东西不适合记忆,在这种情况下,这个问题没有答案.通常,您应该流式传输文件,而不是将其完全存储在内存中.你可能想看看这个权宜之计:http://msdn.microsoft.com/en-us/library/hh285054%28v=vs.110%29.aspx (6认同)
  • .NET中的数组大小有限制,但在.NET 4.5中,您可以使用特殊配置选项打开对大型数组(> 2GB)的支持,请参阅http://msdn.microsoft.com/en-us/library/hh285054的.aspx (4认同)
  • @harag不,这不是问题所在. (3认同)
  • 这不应该是大文件读取的可接受或最高评价的答案,至少是给出的代码.声明"你不应该一次性将整个文件读入内存.你应该以块的形式执行"是正确的,并且应该由代码支持.直到该部分被纠正为止,因为这个答案的代码非常误导并且与那个非常正确的陈述相矛盾. (3认同)

Mar*_*ell 66

我可能会争辩说这里的答案通常是"不要".除非您绝对需要同时使用所有数据,否则请考虑使用Stream基于API的API(或读取器/迭代器的某些变体).当您有多个并行操作(如问题所示)以最小化系统负载和最大化吞吐量时,这一点尤为重要.

例如,如果要将数据流式传输给调用者:

Stream dest = ...
using(Stream source = File.OpenRead(path)) {
    byte[] buffer = new byte[2048];
    int bytesRead;
    while((bytesRead = source.Read(buffer, 0, buffer.Length)) > 0) {
        dest.Write(buffer, 0, bytesRead);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 要添加到您的语句中,我甚至建议考虑异步ASP.NET处理程序,如果您有一个I/O绑定操作,如将文件流式传输到客户端.但是,如果您因为某些原因*必须*将整个文件读取到`byte []`,我建议避免使用流或其他任何东西,只使用系统提供的API. (2认同)

Pow*_*ord 32

我会这样想:

byte[] file = System.IO.File.ReadAllBytes(fileName);
Run Code Online (Sandbox Code Playgroud)

  • 请注意,当获取非常大的文件时,这可能会停止。 (2认同)

小智 25

您的代码可以考虑到这一点(代替File.ReadAllBytes):

public byte[] ReadAllBytes(string fileName)
{
    byte[] buffer = null;
    using (FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
    {
        buffer = new byte[fs.Length];
        fs.Read(buffer, 0, (int)fs.Length);
    }
    return buffer;
} 
Run Code Online (Sandbox Code Playgroud)

请注意Integer.MaxValue - Read方法放置的文件大小限制.换句话说,您一次只能读取2GB的块.

另请注意,FileStream的最后一个参数是缓冲区大小.

我还建议阅读有关FileStreamBufferedStream的内容.

一如既往的简单示例程序,以最快的速度分析将是最有益的.

您的底层硬件也会对性能产生很大影响.您是否使用具有大缓存的服务器硬盘驱动器和带有板载内存缓存的RAID卡?或者您使用连接到IDE端口的标准驱动器?

  • 此代码包含一个严重错误.只需返回至少1个字节即可读取. (2认同)

小智 9

根据操作频率,文件大小以及您正在查看的文件数量,还有其他性能问题需要考虑.要记住的一件事是,每个字节数组都将被垃圾收集器释放.如果您没有缓存任何这些数据,最终可能会造成大量垃圾,并且会使您的大部分性能损失到GC中的时间.如果块大于85K,你将分配大对象堆(LOH),这将需要所有代的集合来释放(这是非常昂贵的,并且在服务器上将停止所有执行,而它正在进行).此外,如果您在LOH上有大量对象,则最终可能会出现LOH碎片(LOH从未压缩),这会导致性能不佳和内存不足异常.一旦达到某一点,您就可以回收该过程,但我不知道这是否是最佳做法.

关键是,您应该考虑应用程序的整个生命周期,然后才能以最快的方式将所有字节读入内存,或者您可能会为整体性能进行短期性能交易.


vap*_*guy 6

我会说BinaryReader很好,但可以重构,而不是用于获取缓冲区长度的所有代码行:

public byte[] FileToByteArray(string fileName)
{
    byte[] fileData = null;

    using (FileStream fs = File.OpenRead(fileName)) 
    { 
        using (BinaryReader binaryReader = new BinaryReader(fs))
        {
            fileData = binaryReader.ReadBytes((int)fs.Length); 
        }
    }
    return fileData;
}
Run Code Online (Sandbox Code Playgroud)

应该比使用更好的.ReadAllBytes(),因为我在包括顶部响应的意见看到.ReadAllBytes(),该提意见的人有问题的文件> 600 MB,因为BinaryReader是为这样的事情.此外,将其放在一个using声明中可确保FileStreamBinaryReader关闭和处理.