不能。Blazor WASM 运行在浏览器沙箱中,无法访问真实文件系统,System.IO 类型会抛出 PlatformNotSupportedException;必须使用内存模拟的虚拟文件系统,如基于 ConcurrentDictionary 实现路径映射与读写。

Blazor WASM 能不能直接用 System.IO 操作真实文件系统?
不能。Blazor WebAssembly 运行在浏览器沙箱里,没有访问本地磁盘的权限,File、Directory、FileStream 等类型会抛出 PlatformNotSupportedException。这不是权限没开,是根本不存在这个能力。
所以“虚拟文件系统”不是绕过限制,而是主动放弃真实 I/O,把数据存在内存里,自己实现路径解析、读写逻辑、目录树维护这些事。
- 常见错误现象:
UnauthorizedAccessException或PlatformNotSupportedException出现在调用File.ReadAllText时 - 使用场景:编辑器类应用(如 Markdown 预览器)、配置管理页、离线数据表单缓存
- 别试图用
JSRuntime.InvokeVoidAsync去调 JS 的FileSystem API——它目前只被 Chrome 实验性支持,且 Blazor WASM 无法拿到持久句柄
用 MemoryFileSystem 类手动建一棵内存目录树
没有现成的“Blazor 虚拟文件系统 NuGet 包”能开箱即用。最稳的方式是自己封装一个轻量级类,用 Dictionary<string byte></string> 存文件内容,用嵌套 Dictionary 或 ConcurrentDictionary 模拟目录结构。
关键不是存得有多像硬盘,而是接口要贴近开发者直觉:支持 /docs/readme.md 这样的路径,能区分文件/目录,能列子项,能检查是否存在。
- 路径分隔符统一用
/(Windows 用户习惯\,但浏览器 URL 和 Blazor 路由都用/,避免混淆) - 目录末尾不加
/(/assets是目录,/assets/是另一个路径,容易引发歧义) - 大小写敏感:WASM 运行环境是 Linux-like,
/A.txt和/a.txt是两个文件 - 示例核心结构:
public class MemoryFileSystem { private readonly ConcurrentDictionary<string, (bool IsDirectory, byte[]? Content)> _entries = new(); <pre class="brush:php;toolbar:false;">public void WriteFile(string path, byte[] content) => _entries[path] = (IsDirectory: false, Content: content); public byte[]? ReadFile(string path) => _entries.TryGetValue(path, out var entry) && !entry.IsDirectory ? entry.Content : null;}
怎么让 InputFile 上传的内容进虚拟文件系统?
InputFile 组件选中的文件默认只提供 IBrowserFile 接口,它不暴露底层路径,只能读取流。你要做的就是把它转成字节数组,再塞进自己的 MemoryFileSystem。
注意:别直接调 browserFile.OpenReadStream() 后长期持有 Stream——它是一次性流,且 Blazor 不管理其生命周期,容易泄漏或报 ObjectDisposedException。
- 必须立刻读完并保存为
byte[]:var file = browserFile; using var stream = file.OpenReadStream(maxAllowedSize: 10_000_000); var buffer = new byte[file.Size]; await stream.ReadAsync(buffer); memoryFs.WriteFile($"/uploads/{file.Name}", buffer); - 路径拼接别用
Path.Combine(它依赖 OS,WASM 下行为不可靠),手写字符串拼接或用string.Join("/", parts) - 如果需要保留原始文件名编码(比如含中文、emoji),
browserFile.Name是 URL-decoded 后的,可直接用
路由和静态资源冲突怎么办?
Blazor WASM 的 _Host.cshtml 把所有未匹配路由都 fallback 到 index.html,这会导致你设计的虚拟路径(如 /data/config.json)被当成前端路由处理,而不是返回文件内容。
解决思路不是改服务器配置(WASM 可能跑在 GitHub Pages 这类无后端环境),而是把虚拟文件读取逻辑收口到一个服务里,由组件按需调用,不走 HTTP 请求。
- 不要尝试注册自定义
HttpMessageHandler拦截fetch请求——它无法拦截同域下的普通fetch("/data/a.txt"),因为浏览器已把请求发出去了 - 正确做法:所有“读虚拟文件”操作都通过 C# 方法调用,比如
memoryFs.ReadFile("/data/a.txt"),返回Task<byte></byte> - 如果非要用
fetch(比如给第三方 JS 库用),就得在wwwroot/index.html里加一段内联 JS,劫持window.fetch,对特定路径前缀(如/vfs/)返回 mock 响应——但这层 JS 和 C# 内存状态同步很脆弱,不推荐
真正难的不是实现读写,是怎么让“虚拟路径”在组件生命周期、导航、状态恢复之间保持一致。比如用户刷新页面后,内存里的文件全丢了——这时候得配合 localStorage 做序列化,但要注意二进制内容得 base64 编码,而且有 5MB 上限。









