FileStream是底层字节流,非文本操作专用;必须用它时包括:大文件局部读取、多进程并发访问、随机Seek、缓冲区精确控制;FileMode与FileAccess需严格匹配,FileShare决定跨进程共享能力,且必须用using确保Dispose释放句柄。

FileStream 不是用来“读字符串”或“写文本”的,它是字节流的底层通道。直接用它读 string 会乱码,不设 FileShare 会锁死文件,忘了 using 会导致后续 File.Delete 报 IOException —— 这些不是配置问题,是设计契约。
什么时候非用 FileStream 不可?
你遇到以下任一场景,File.ReadAllText 或 File.WriteAllBytes 就该让位:
- 要从第 8192 字节开始读取视频帧头,而不是加载整个 2GB MP4 到内存
- 多个进程需同时读写日志文件(一个写,多个分析工具在读)
- 需要反复跳转:比如解析数据库快照文件,先读 4 字节长度,再 Seek 到偏移量读实际数据块
- 写入时必须控制缓冲区大小(例如嵌入式设备只允许 4KB 缓冲)
这些需求都绕不开 Position、Seek、FileShare 和显式缓冲区控制——而静态 File 方法压根不暴露这些能力。
FileMode 和 FileAccess 搭配错了会立刻失败
FileMode 决定“文件是否存在时怎么处理”,FileAccess 决定“我能对它干啥”,两者不匹配会在构造时直接抛异常:
-
FileMode.Append必须配FileAccess.Write,配Read会 throwArgumentException -
FileMode.Truncate要求文件已存在,否则抛FileNotFoundException -
FileMode.CreateNew遇到同名文件直接 throwIOException,不是覆盖 - 用
FileAccess.Read打开一个只写权限的文件(如 NTFS ACL 限制),会触发UnauthorizedAccessException
最安全的通用组合是:FileMode.OpenOrCreate + FileAccess.ReadWrite,但注意:它不会清空已有内容,只是确保文件存在且可读写。
FileShare 是并发访问的开关,不是可选项
默认 FileShare.None 意味着“我独占这个文件”,哪怕你只是 FileAccess.Read,其他线程调 new FileStream(..., FileAccess.Read) 也会卡住或抛 IOException: The process cannot access the file。
- 想让其他进程能同时读:用
FileShare.Read - 想让其他进程能同时写(需自行同步):用
FileShare.Write - 日志场景常见组合:
FileMode.Append+FileAccess.Write+FileShare.Read
别指望靠 lock 语句解决跨进程冲突——FileShare 是操作系统级的句柄共享策略,lock 只管本进程内线程。
Dispose 不等于 Close,不用 using 就等于留后门
Close() 只是把内部状态设为 Closed,不释放 OS 句柄;Dispose() 才真正调用 SafeFileHandle.Dispose()。GC 不保证及时回收后者,后果很实在:
- 文件被占用,
File.Move或File.Delete失败 - Windows 下未释放句柄数超 16384,新
FileStream构造直接失败 - 长时间运行服务中出现“句柄泄漏”,内存没涨但 I/O 逐渐变慢
唯一可靠写法只有这一种:
using (var fs = new FileStream("data.bin", FileMode.Open, FileAccess.Read, FileShare.Read))
{
var buffer = new byte[4096];
int read = fs.Read(buffer, 0, buffer.Length);
}
别信“我手动调了 fs.Close()”,也别在 finally 里只写 fs.Close()——Dispose() 才是释放句柄的那条指令。


















