创建 filestream 时应匹配 filemode 和 fileaccess:如 filemode.append + fileaccess.write 追加日志;filemode.openorcreate + fileaccess.readwrite 读写二进制;filemode.open + fileaccess.read + fileshare.read 允许多进程只读。

FileStream 构造函数参数怎么选
创建 FileStream 时,最关键的不是“能不能读写”,而是「打开方式」和「访问权限」是否匹配你的操作意图。比如用 FileMode.Open + FileAccess.Read 去尝试写入,运行时会直接抛出 UnauthorizedAccessException;反过来,用 FileAccess.Write 去读取已存在的只读文件,也会失败。
常见组合建议:
-
new FileStream("log.txt", FileMode.Append, FileAccess.Write):追加日志,安全且高效 -
new FileStream("data.bin", FileMode.OpenOrCreate, FileAccess.ReadWrite):二进制数据读写,文件不存在就新建 -
new FileStream("config.json", FileMode.Open, FileAccess.Read, FileShare.Read):允许多个进程同时读(但不能写)
注意:FileShare.None 是默认值,意味着一旦你打开,其他任何进程都无法再访问该文件——哪怕只是想读个大小,也会被阻塞或报 IOException。
用 FileStream 读取文本文件容易踩的坑
FileStream 本身不处理字符编码,它只管字节。直接用 Read 方法读出来的是 byte[],如果你把它当字符串用(比如 Encoding.UTF8.GetString(bytes)),却没考虑 BOM、换行符、缓冲区截断等问题,结果常是乱码或丢数据。
更稳妥的做法:
- 纯文本场景,优先用
StreamReader包一层:using var fs = new FileStream("a.txt", FileMode.Open); using var sr = new StreamReader(fs, Encoding.UTF8); string line = sr.ReadLine(); - 必须用
FileStream.Read时,确保缓冲区足够大,并检查返回值(实际读到的字节数),不要假设一次就读完整个文件 - 读取小文件且确定编码时,可用
File.ReadAllBytes()+Encoding解码,比手动管理FileStream更少出错
写入时 flush 和 close 的顺序影响数据落地
FileStream 默认启用内部缓冲,Write 调用后数据未必立刻写入磁盘。如果只调用 Flush() 而不 Close() 或 Dispose(),某些场景下(尤其是网络驱动器或杀毒软件介入时)仍可能丢失最后几KB。
正确姿势:
- 始终用
using语句块自动释放:using (var fs = new FileStream(...)) { fs.Write(...); }—— 这会隐式调用Dispose(),等价于Flush() + Close() - 避免单独调用
Flush()后继续写入,除非你明确需要“阶段性落盘”且能接受后续异常导致中间状态不一致 - 对关键日志或配置写入,可加
fs.Flush(true)(即FlushToDisk),强制绕过系统缓存,但会明显降低性能
异步读写 FileStream 必须开启 FileOptions.Asynchronous
很多人以为只要调用 ReadAsync 或 WriteAsync 就是真异步,结果发现线程还是被卡住。根本原因是:Windows 上 .NET 的异步 I/O 依赖底层 IOCP,而默认构造的 FileStream 不启用异步标志,此时 ReadAsync 实际是在线程池里同步执行再包装成 Task。
启用真正异步 I/O 的写法:
new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None, 4096, FileOptions.Asynchronous)- 缓冲区大小(第5个参数)建议设为 4096 或其整数倍,太小会频繁调度,太大浪费内存
- Linux/macOS 上该标志被忽略,但无害;.NET 6+ 对跨平台异步支持更好,但仍建议显式指定
没加 FileOptions.Asynchronous 时,ReadAsync 表面是 async,实际是伪异步,压测下容易暴露线程饥饿问题。










