应使用 filestream 配合固定缓冲区分块写入,禁用 streamwriter.autoflush 并手动控制刷新时机,结合异步节流、yield return 流式生成及 utf-8 字节级编码优化内存占用。

用 FileStream 配合缓冲区写入,别用 File.WriteAllText
大文件写入最常见错误就是调用 File.WriteAllText 或 StringBuilder.ToString() 拼接完整内容再写——这等于把整个文件塞进内存。实际应边生成边写,靠固定大小缓冲区控制内存占用。
关键不是“能不能写”,而是“每次只让多少字节待在内存里”。
推荐做法:FileStream 构造时传入 bufferSize(如 8192),并始终用 Write 或 WriteAsync 分块推送,不累积全文本。
StreamWriter 必须关掉 AutoFlush 并手动控制刷新时机
很多人以为 StreamWriter 默认就流式写入,其实它内部有缓冲区,默认 AutoFlush = false,但若你不显式调用 Flush() 或 Dispose(),最后一段数据可能卡在缓冲区没落盘——尤其程序异常退出时直接丢数据。
更糟的是:设 AutoFlush = true 会导致每写一行都刷磁盘,I/O 暴增,性能崩塌。
正确姿势:
- 构造
StreamWriter时传入bufferSize(如new StreamWriter(fs, Encoding.UTF8, 65536)) - 写入过程中避免频繁
Flush();按逻辑批次刷(例如每写 1000 行或每 1MB) - 务必用
using确保Dispose()被调用,它会自动Flush()+ 关闭底层FileStream
异步写入要防住 async/await 堆积和未完成任务
用 WriteAsync 是对的,但容易忽略两个现实问题:
一是连续 await writer.WriteAsync(...) 实际仍是串行,没提升吞吐;二是如果上游数据源(比如数据库游标、网络流)也异步,没做节流可能把待写任务堆满内存。
实操建议:
- 用
Task.WhenAll控制并发写入数(如最多同时 3 个WriteAsync任务) - 写入前检查
stream.Length或用Position跟踪偏移,避免因异常导致重复写或跳字节 - 别在循环里无条件
await;可先Task.Run把数据序列化成字节数组,再await stream.WriteAsync(),分离 CPU 和 I/O
分块生成内容时,警惕字符串拼接和 LINQ 的隐式内存开销
流式写入的前提是“内容能一块块产出”,但很多人仍习惯先 list.Select(...).ToArray() 或 string.Join 拼整块文本——这又回到内存爆炸原点。
真正低内存的做法是用 yield return 构建可枚举的数据流,配合 foreach 边取边写:
foreach (var item in GenerateRecords())
{
await writer.WriteLineAsync(item.ToCsvLine());
}
其中 GenerateRecords() 返回 IEnumerable<t></t>,且内部不缓存全部结果;若来源是数据库,就用 SqlDataReader.Read() 逐行读+逐行写,绝不 DataTable.Load()。
最容易被忽略的一点:UTF-8 编码下中文字符占 3 字节,但 StreamWriter 默认缓冲区按字符计,不是字节——如果你写大量中文,实际内存驻留可能比预估高 2–3 倍。必要时改用 Span<byte></byte> + Encoding.UTF8.GetBytes 手动编码再写入原始字节流。










