使用JsonSerializer.Serialize(Utf8JsonWriter)写入JSON文件后文件被占用无法访问的问题排查
这个问题的核心原因是你没有正确释放File.Create()返回的FileStream资源,导致文件句柄没有被系统及时回收,从而引发文件被占用的异常。
为什么第一个写法会出问题?
在你的第一段代码里:
string pathOfRelevantStuff = "path"; string fileOfRelevantStuff = Path.Combine(pathOfRelevantStuff, $"fileName_1.json"); Utf8JsonWriter relevantDataWriter = new(File.Create(fileOfRelevantStuff)); JsonSerializer.Serialize(relevantDataWriter, dataToBeReadLater); relevantDataWriter.Flush(); relevantDataWriter.Dispose();
你直接把File.Create()返回的FileStream传给了Utf8JsonWriter的构造函数,但没有对这个FileStream做任何资源管理操作。虽然你手动调用了relevantDataWriter.Dispose(),但在.NET 7的Utf8JsonWriter实现中,即使默认配置会在Dispose时尝试关闭底层流,仍可能存在边界情况导致流没有被正确释放——更关键的是,手动管理资源的方式本身就不可靠,很容易因为代码路径变化(比如中途抛出异常)导致资源泄漏。
你调试时在Dispose()之后仍无法打开文件,就是因为底层的文件句柄还没被系统彻底释放。
为什么Workaround能正常工作?
你的第二个写法用StreamWriter结合File.CreateText(),并且手动调用了Close()和Dispose():
StreamWriter relevantDataWriter = File.CreateText(fileOfRelevantStuff); // ... relevantDataWriter.Close(); relevantDataWriter.Dispose();
File.CreateText()返回的StreamWriter会自动绑定并管理底层的文件流,调用Close()或Dispose()时会确保底层文件句柄被正确释放,所以不会出现文件占用的问题。
正确的修复方式
最可靠的做法是使用**using语句**自动管理所有实现IDisposable接口的资源(包括FileStream和Utf8JsonWriter),这样无论代码是否抛出异常,资源都会被自动释放:
string pathOfRelevantStuff = "path"; string fileOfRelevantStuff = Path.Combine(pathOfRelevantStuff, $"fileName_1.json"); // 使用using包裹FileStream和Utf8JsonWriter,确保资源自动释放 using var fileStream = File.Create(fileOfRelevantStuff); using var relevantDataWriter = new Utf8JsonWriter(fileStream); JsonSerializer.Serialize(relevantDataWriter, dataToBeReadLater); // 不需要手动调用Flush和Dispose,using块结束时会自动处理
如果一定要手动管理资源(不推荐),需要确保同时释放FileStream和Utf8JsonWriter:
string pathOfRelevantStuff = "path"; string fileOfRelevantStuff = Path.Combine(pathOfRelevantStuff, $"fileName_1.json"); FileStream fileStream = null; Utf8JsonWriter relevantDataWriter = null; try { fileStream = File.Create(fileOfRelevantStuff); relevantDataWriter = new Utf8JsonWriter(fileStream); JsonSerializer.Serialize(relevantDataWriter, dataToBeReadLater); relevantDataWriter.Flush(); } finally { relevantDataWriter?.Dispose(); fileStream?.Dispose(); }
额外提示
Utf8JsonWriter.Dispose()会自动调用Flush(),所以手动调用Flush()是多余的,可以省略。- 始终优先使用
using语句(C# 8+支持using var语法,更简洁)来管理可释放资源,这是.NET中避免资源泄漏的最佳实践。 - 你遇到的问题在.NET 7的后续版本中可能已经被修复,但使用
using语句的写法在任何版本中都是安全可靠的。
备注:内容来源于stack exchange,提问作者Jean-David Lanz

