0

0

C# 实现自己的虚拟文件系统 C#如何创建一个内存或数据库支持的IFileProvider

月夜之吻

月夜之吻

发布时间:2026-02-17 08:30:11

|

549人浏览过

|

来源于php中文网

原创

ifileprovider 不能直接 new 因为它是抽象接口,.net 未提供内存或数据库实现;需自定义实现 getdirectorycontents、getfileinfo 和 watch 方法,并注意线程安全、路径大小写及同步/异步兼容性。

c# 实现自己的虚拟文件系统 c#如何创建一个内存或数据库支持的ifileprovider

为什么 IFileProvider 不能直接 new 出来

因为 IFileProvider 是抽象接口,.NET 没提供开箱即用的内存或数据库实现。你看到的 PhysicalFileProviderEmbeddedFileProvider 都只负责读取物理路径或程序集资源,不支持写入、动态挂载或持久化到数据库。

想用内存或数据库做后端,必须自己实现 IFileProvider 和配套的 IFileInfo —— 这不是配置问题,是补全缺失抽象的责任。

  • 常见错误现象:new PhysicalFileProvider("memory://") 报错或静默失败,因为协议前缀不被识别
  • 根本原因:所有内置 IFileProvider 实现都依赖 System.IO,而 System.IO 不处理内存/DB 路径语义
  • 关键点:你得重写 GetDirectoryContentsGetFileInfoWatch 三个方法,其中 Watch 在内存场景可返回空实现

内存版 IFileProvider 怎么写才不崩

核心是用 ConcurrentDictionary<string byte></string> 存文件内容,再套一层线程安全的 IFileInfo 实现。别用 Dictionary,并发读写会丢数据;也别在 GetFileInfo 里每次 new 对象,容易 GC 压力大。

示例关键片段:

360智绘
360智绘

360智脑推出的AI绘画创作与分享平台

下载
public class MemoryFileProvider : IFileProvider
{
    private readonly ConcurrentDictionary<string, byte[]> _files = new();

    public IDirectoryContents GetDirectoryContents(string subpath)
    {
        var entries = _files.Keys
            .Where(k => k.StartsWith(subpath + "/", StringComparison.Ordinal))
            .Select(k => new MemoryFileInfo(k, _files[k]))
            .ToArray();
        return new MemoryDirectoryContents(entries);
    }

    public IFileInfo GetFileInfo(string subpath) =>
        _files.TryGetValue(subpath, out var data)
            ? new MemoryFileInfo(subpath, data)
            : new NotFoundFileInfo(subpath);
}
  • subpath 传进来默认不带开头斜杠,但你的内存 key 可以统一存为 "wwwroot/index.html" 这种格式,别自动拼 /
  • 注意大小写:Windows 下路径不区分大小写,但 ConcurrentDictionary 默认区分,建议用 StringComparer.OrdinalIgnoreCase 构造
  • MemoryFileInfo 必须实现 ExistsLengthCreateReadStream,其中 CreateReadStream 返回 new MemoryStream(data),别复用同一份 MemoryStream

数据库支持的 IFileProvider 要绕过哪些坑

数据库本质是异步 IO,但 IFileProvider 全是同步方法。硬塞 GetAwaiter().GetResult() 会死锁(尤其在 ASP.NET Core 请求上下文中)。唯一安全做法:用同步 DB 驱动(如 SQLite 的 Microsoft.Data.Sqlite 同步 API),或者把 DB 层包装成同步假象。

  • 别用 EntityFrameworkCore 直接查:它没有同步查询入口,强行 .Result 在 IIS 或 Kestrel 下极易卡主线程
  • 表结构至少要字段:Path NVARCHAR(450) PRIMARY KEYContent BLOBLastModified DATETIME2Path 建唯一索引,否则 GetFileInfo 查太慢
  • GetDirectoryContents 不能用 LIKE 'prefix%' 模糊查——路径有层级,得按 / 分割后做前缀树或递归 CTE,简单做法是加一列 ParentPath 存上级目录
  • 如果文件可能超 10MB,别把 Content 字段全 load 到内存再给 CreateReadStream,改用 SqliteCommandExecuteReader(CommandBehavior.SequentialAccess) 流式读取

IFileProvider 替换进 ASP.NET Core 时为啥静态文件不生效

因为 UseStaticFiles() 默认只认 IWebHostEnvironment.WebRootFileProvider,你 new 好的自定义 provider 得显式塞进去,不是注册到 DI 容器就完事。

  • 正确姿势:services.AddSingleton<ifileprovider>(sp => new DatabaseFileProvider(...));</ifileprovider> 注册,然后在 Configure 里:app.UseStaticFiles(new StaticFileOptions { FileProvider = app.ApplicationServices.GetRequiredService<ifileprovider>() });</ifileprovider>
  • 容易忽略:如果你还用了 UseSpa()UseBlazorServer(),它们内部也会创建自己的 FileProvider,得看文档找对应选项覆盖,比如 spa.Options.SourcePath 是路径,不是 provider
  • 调试技巧:在自定义 GetFileInfo 开头加 Console.WriteLine($"GetFileInfo({subpath})"),启动后访问 /favicon.ico,看有没有输出——没输出说明根本没走到你的 provider

最麻烦的其实是 Watch 机制:内存和数据库都没法高效监听变更,ChangeToken.OnChange 那套得你自己用定时轮询或 DB 触发器模拟,生产环境慎用。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
硬盘接口类型介绍
硬盘接口类型介绍

硬盘接口类型有IDE、SATA、SCSI、Fibre Channel、USB、eSATA、mSATA、PCIe等等。详细介绍:1、IDE接口是一种并行接口,主要用于连接硬盘和光驱等设备,它主要有两种类型:ATA和ATAPI,IDE接口已经逐渐被SATA接口;2、SATA接口是一种串行接口,相较于IDE接口,它具有更高的传输速度、更低的功耗和更小的体积;3、SCSI接口等等。

1486

2023.10.19

PHP接口编写教程
PHP接口编写教程

本专题整合了PHP接口编写教程,阅读专题下面的文章了解更多详细内容。

403

2025.10.17

php8.4实现接口限流的教程
php8.4实现接口限流的教程

PHP8.4本身不内置限流功能,需借助Redis(令牌桶)或Swoole(漏桶)实现;文件锁因I/O瓶颈、无跨机共享、秒级精度等缺陷不适用高并发场景。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2225

2025.12.29

java接口相关教程
java接口相关教程

本专题整合了java接口相关内容,阅读专题下面的文章了解更多详细内容。

37

2026.01.19

length函数用法
length函数用法

length函数用于返回指定字符串的字符数或字节数。可以用于计算字符串的长度,以便在查询和处理字符串数据时进行操作和判断。 需要注意的是length函数计算的是字符串的字符数,而不是字节数。对于多字节字符集,一个字符可能由多个字节组成。因此,length函数在计算字符串长度时会将多字节字符作为一个字符来计算。更多关于length函数的用法,大家可以阅读本专题下面的文章。

950

2023.09.19

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

675

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

675

2023.08.10

console接口是干嘛的
console接口是干嘛的

console接口是一种用于在计算机命令行或浏览器开发工具中输出信息的工具,提供了一种简单的方式来记录和查看应用程序的输出结果和调试信息。本专题为大家提供console接口相关的各种文章、以及下载和课程。

419

2023.08.08

pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

462

2026.02.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
C# 教程
C# 教程

共94课时 | 9.6万人学习

C 教程
C 教程

共75课时 | 4.8万人学习

C++教程
C++教程

共115课时 | 18.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号