Alh*_*dos 9 c# bytearray download out-of-memory filestream
我正在使用Filestream读取大文件(> 500 MB),我得到了OutOfMemoryException.
任何解决方案.
我的代码是:
using (var fs3 = new FileStream(filePath2, FileMode.Open, FileAccess.Read))
{
byte[] b2 = ReadFully(fs3, 1024);
}
public static byte[] ReadFully(Stream stream, int initialLength)
{
// If we've been passed an unhelpful initial length, just
// use 32K.
if (initialLength < 1)
{
initialLength = 32768;
}
byte[] buffer = new byte[initialLength];
int read = 0;
int chunk;
while ((chunk = stream.Read(buffer, read, buffer.Length - read)) > 0)
{
read += chunk;
// If we've reached the end of our buffer, check to see if there's
// any more information
if (read == buffer.Length)
{
int nextByte = stream.ReadByte();
// End of stream? If so, we're done
if (nextByte == -1)
{
return buffer;
}
// Nope. Resize the buffer, put in the byte we've just
// read, and continue
byte[] newBuffer = new byte[buffer.Length * 2];
Array.Copy(buffer, newBuffer, buffer.Length);
newBuffer[read] = (byte)nextByte;
buffer = newBuffer;
read++;
}
}
// Buffer is now too big. Shrink it.
byte[] ret = new byte[read];
Array.Copy(buffer, ret, read);
return ret;
}
Run Code Online (Sandbox Code Playgroud)
Che*_*eso 33
您显示的代码将500mb文件的所有内容读入内存中的连续区域.你得到一个内存不足的情况并不奇怪.
解决方案是"不要那样做".
你真的想做什么?
如果要完全读取文件,它比您使用的ReadFully方法简单得多.试试这个:
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
byte[] buffer = new byte[fs.Length];
int bytesRead = fs.Read(buffer, 0, buffer.Length);
// buffer now contains the entire contents of the file
}
Run Code Online (Sandbox Code Playgroud)
但是......使用此代码无法解决您的问题.它可能适用于500mb文件.它不适用于750mb文件或1gb文件.在某些时候,您将达到系统内存的限制,并且您将遇到与开始时相同的内存不足错误.
问题是您试图一次将文件的全部内容保存在内存中.这通常是不必要的,并且随着文件大小的增加注定要失败.当文件大小为16k时没问题.在500mb,这是错误的方法.
这就是我多次问过你为什么要做的原因?
听起来好像你想将文件的内容发送到ASPNET响应流.这是个问题.不是"如何将500mb文件读入内存?" 但"如何将大文件发送到ASPNET响应流?"
为此,再一次,它相当简单.
// emit the contents of a file into the ASPNET Response stream
using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
Response.BufferOutput= false; // to prevent buffering
byte[] buffer = new byte[1024];
int bytesRead = 0;
while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0)
{
Response.OutputStream.Write(buffer, 0, bytesRead);
}
}
Run Code Online (Sandbox Code Playgroud)
它的作用是迭代地从文件中读取一个块,并将该块写入Response流,直到文件中没有其他内容可读.这就是"流式IO"的含义.数据通过你的逻辑,但永远不会全部放在一个地方,就像水流通过水闸一样.在这个例子中,一次内存中永远不会有超过1k的文件数据(好吧,不管你的应用程序代码是不是这样.堆栈中还有其他IO缓冲区.)
这是流式IO中的常见模式.学习它,使用它.
将数据输出到ASPNET的Response.OutputStream时的一个技巧是设置BufferOutput = false
.默认情况下,ASPNET尝试缓冲其输出.在这种情况下(500mb文件),缓冲是一个坏主意.将该BufferOutput
属性设置为false将阻止ASPNET在发送第一个字节之前尝试缓冲所有文件数据.当您知道发送的文件非常大时,请使用它.数据仍将正确发送到浏览器.
即使这不是完整的解决方案.您需要设置响应标头等.不过,我猜你已经意识到了这一点.
每次重新分配时,缓冲区大小都会加倍,这意味着以前分配的块永远无法使用(它们实际上会泄漏)。当您达到 500 MB 时,您已经消耗了 1 GB 加上开销。事实上,它可能是 2 GB,因为如果您达到 512 MB,您的下一个分配将是 1 GB。在 32 位系统上,这会破坏您的进程。
由于您正在读入的是一个普通文件,因此只需查询文件系统的大小并一次性预分配缓冲区即可。
归档时间: |
|
查看次数: |
29715 次 |
最近记录: |