C#只追加文件(Append-Only) C#如何创建只能在末尾写入的文件

来源:这里教程网 时间:2026-02-21 17:42:35 作者:

FileMode.Append
打开文件时,写入位置自动跳转到末尾

这是最直接的方式:只要用

FileMode.Append
打开文件流,系统会强制将写入位置设为文件末尾,无论你之前是否调用过
Seek()
或设置了
Position
。注意,
Append
模式只允许写入,不支持读取(除非额外指定
FileAccess.ReadWrite
,但那样就失去“只追加”语义了)。

常见错误是误以为

FileMode.Append
+
FileAccess.Read
可行——这会抛出
System.ArgumentException: 'Access to the path is denied.'
。必须搭配
FileAccess.Write
FileAccess.ReadWrite

FileStream fs = new FileStream("log.txt", FileMode.Append, FileAccess.Write, FileShare.Read)
—— 安全、典型用法
若需写完立刻刷盘,记得调用
fs.Flush()
;否则依赖缓冲,崩溃时可能丢最后几条日志
多线程写同一文件时,
FileMode.Append
本身不保证原子性,需外加
lock
或用
StreamWriter
配合
TextWriter.Synchronized

StreamWriter
构造函数传
true
启用追加模式

StreamWriter
的构造函数第二个布尔参数为
true
时,底层自动使用
FileMode.Append
FileAccess.Write
,行为一致但更简洁。它还会自动处理编码和换行符,适合文本日志场景。

容易忽略的是:如果文件不存在,

StreamWriter
会自动创建;但若目录不存在,会抛出
DirectoryNotFoundException
,得提前用
Directory.CreateDirectory()

using var sw = new StreamWriter("data.log", true) { AutoFlush = true }
—— 推荐加
AutoFlush = true
避免缓冲丢失
不要手动调用
sw.BaseStream.Seek()
,即使成功也不会改变后续
WriteLine()
的行为——它始终追加
避免在循环里反复新建
StreamWriter
实例,频繁打开/关闭文件影响性能;应复用实例或用对象池

Windows 上通过 ACL 限制写权限实现系统级只追加

仅靠 .NET 流模式无法阻止其他进程或用户用记事本、PowerShell 等工具修改文件中间内容。真要 enforce “只能追加”,得结合 Windows 文件系统权限(ACL):移除用户的

Modify
权限,只保留
WriteData
(即追加)和
ReadData

这需要管理员权限,且仅适用于 NTFS。代码中可用

FileSecurity
类操作,但要注意:设置 ACL 后,普通用户调用
File.WriteAllText()
会直接失败,而
FileMode.Append
仍可成功——这才是你想要的效果。

File.GetAccessControl()
获取当前 ACL,再用
SetAccessRule()
移除
FileSystemRights.Modify
务必保留
FileSystemRights.AppendData
,否则
FileMode.Append
也会被拒绝
Linux/macOS 不支持该机制,跨平台方案只能靠外部守护进程或专用日志服务(如 systemd-journald)

注意
FileMode.Append
不等于“不可覆盖”

追加模式下,你不能指定偏移写入,但仍有几个边界情况可能导致非末尾写入:一是用

FileStream
Write()
写入长度为 0 的字节数组(无副作用);二是文件被其他进程截断(
SetLength(0)
),此时下次追加会从新末尾(即开头)开始——这不是 .NET 的 bug,而是底层 OS 行为。

所以真正的只追加日志系统,通常还需配合:文件名带时间戳防重名、定期归档压缩、用独立进程监控文件大小突变、或改用内存映射文件(

MemoryMappedFile
)+ 自定义写入协议。

别指望单靠一个 FileMode 就搞定所有安全与一致性需求。追加只是起点,后续的防护、审计、恢复逻辑,才是关键。

相关推荐