在C#开发中,处理超过几百MB甚至GB级别的大文件时,如果一次性将文件内容加载到内存中,会导致内存占用飙升,甚至引发OutOfMemoryException异常。FileStream作为.NET中用于文件流操作的核心类,支持按字节分批读写文件,能够有效控制内存占用,是处理大文件的首选方案。

FileStream基础读写原理
FileStream基于流的操作模式,不会一次性加载整个文件,而是通过缓冲区逐段处理文件内容。它的核心参数包括文件路径、文件模式、访问权限和缓冲区大小,合理设置这些参数能够直接影响大文件读写的效率。
核心参数说明
| 参数名 | 作用说明 |
|---|---|
| FileMode | 指定文件的打开方式,如Create创建新文件、Open打开现有文件、Append追加内容等 |
| FileAccess | 指定文件的访问权限,如Read只读、Write只写、ReadWrite读写 |
| bufferSize | 设置读写缓冲区的大小,默认值是4096字节,大文件场景可适当调大 |
使用FileStream读取大文件
读取大文件时,需要循环从流中读取数据,直到读取完所有内容,每次读取的字节数由缓冲区大小决定,避免一次性加载过多数据。
同步读取示例
以下代码演示了使用FileStream同步读取大文件并输出内容长度的过程:
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = @"D:largefile.dat";
// 设置缓冲区大小为8KB,可根据文件大小调整
int bufferSize = 8192;
// 以只读方式打开文件,使用异步模式提升性能
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, useAsync: true))
{
byte[] buffer = new byte[bufferSize];
int bytesRead;
long totalBytes = 0;
// 循环读取文件内容
while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0)
{
totalBytes += bytesRead;
// 这里可以对读取到的buffer内容做业务处理,比如解析、转存等
// 注意不要直接拼接所有字节到字符串,避免内存占用过高
}
Console.WriteLine($"文件总大小:{totalBytes} 字节");
}
}
}
异步读取示例
异步读取可以避免阻塞主线程,适合在UI程序或者需要同时处理多个任务的场景中使用:
using System;
using System.IO;
using System.Threading.Tasks;
class Program
{
static async Task Main()
{
string filePath = @"D:largefile.dat";
int bufferSize = 16384; // 16KB缓冲区
using (FileStream fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read, bufferSize, useAsync: true))
{
byte[] buffer = new byte[bufferSize];
int bytesRead;
long totalBytes = 0;
while ((bytesRead = await fs.ReadAsync(buffer, 0, buffer.Length)) > 0)
{
totalBytes += bytesRead;
// 异步处理读取到的数据
await ProcessBufferAsync(buffer, bytesRead);
}
Console.WriteLine($"异步读取完成,总大小:{totalBytes} 字节");
}
}
static async Task ProcessBufferAsync(byte[] buffer, int length)
{
// 模拟异步处理数据的过程
await Task.Delay(1);
}
}
使用FileStream写入大文件
写入大文件时同样需要分批写入,避免一次性构造过大的字节数组。如果是生成新文件,需要选择Create模式,如果是追加内容则选择Append模式。
同步写入示例
以下代码演示了将多个小数据块分批写入大文件的过程:
using System;
using System.IO;
class Program
{
static void Main()
{
string outputPath = @"D:output_largefile.dat";
int bufferSize = 8192;
// 创建新文件,如果文件存在则覆盖
using (FileStream fs = new FileStream(outputPath, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize, useAsync: false))
{
// 模拟生成多段数据并写入
for (int i = 0; i < 1000; i++)
{
// 生成一段测试数据,实际场景替换为真实业务数据
byte[] data = new byte[bufferSize];
new Random().NextBytes(data);
fs.Write(data, 0, data.Length);
// 定期刷新缓冲区,确保数据及时写入磁盘
if (i % 100 == 0)
{
fs.Flush();
}
}
}
Console.WriteLine("文件写入完成");
}
}
大文件读写实用技巧
- 合理设置缓冲区大小:默认4KB缓冲区对于大文件偏小,通常可以设置为8KB到64KB之间,具体数值可以根据文件大小和磁盘性能测试调整,过大会增加内存占用,过小会增加IO次数。
- 优先使用异步方法:在读写大文件时,异步操作可以避免阻塞线程,尤其是在UI应用或者高并发服务中,能够提升程序的响应能力。
- 及时释放资源:一定要使用using语句包裹FileStream,确保流在使用完成后自动释放,避免文件句柄泄露,导致后续无法访问文件。
- 避免不必要的类型转换:如果不需要处理文本内容,尽量以字节形式操作,减少字节到字符串的转换开销,避免额外的内存分配。
- 分块处理业务数据:读取到缓冲区数据后,立即处理当前块的内容,处理完成后再读取下一块,不要缓存所有读取到的数据。
常见问题解答
为什么读取大文件时还是出现内存溢出?
如果出现内存溢出,通常是因为在读取过程中缓存了所有读取到的字节数据,比如将所有字节拼接成一个大的字节数组或者字符串。正确的做法是每读取一块数据就处理一块,处理完成后释放该块数据的引用。
FileStream和File.ReadAllBytes有什么区别?
File.ReadAllBytes会一次性将整个文件加载到内存中,只适合小文件;而FileStream支持分批读写,内存占用可控,适合大文件场景。
注意:操作大文件时需要确保磁盘有足够的剩余空间,尤其是写入大文件时,提前检查磁盘空间可以避免写入过程中出现异常。
C#FileStream大文件读写文件流操作修改时间:2026-06-19 11:09:37