导读:本期聚焦于小伙伴创作的《C#如何在ASP.NET Core中实现内存或数据库中的虚拟文件提供程序》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C#如何在ASP.NET Core中实现内存或数据库中的虚拟文件提供程序》有用,将其分享出去将是对创作者最好的鼓励。

在ASP.NET Core应用开发中,默认的文件提供程序基于物理文件系统,只能访问服务器磁盘上的文件。但很多场景下我们需要提供动态生成的文件、内存中临时存储的文件或者存储在数据库中的文件,这时候就需要自定义虚拟文件提供程序来实现需求。

C#如何在ASP.NET Core中实现内存或数据库中的虚拟文件提供程序

核心接口说明

ASP.NET Core的文件系统基于IFileProvider接口,自定义虚拟文件提供程序需要实现该接口以及相关的辅助接口:

  • IFileProvider:核心接口,负责提供文件目录和文件信息
  • IFileInfo:表示单个文件的信息,包含是否存在、长度、读取流等属性
  • IDirectoryContents:表示目录下的文件集合,用于遍历目录内容

实现内存虚拟文件提供程序

第一步:实现内存文件信息类

首先创建InMemoryFileInfo类实现IFileInfo接口,用于描述内存中的文件信息:

using Microsoft.Extensions.FileProviders;
using System.IO;

public class InMemoryFileInfo : IFileInfo
{
    private readonly byte[] _fileContent;

    public InMemoryFileInfo(string name, byte[] content)
    {
        Name = name;
        _fileContent = content;
        Exists = true;
        Length = content.Length;
        LastModified = DateTimeOffset.UtcNow;
        PhysicalPath = null;
        IsDirectory = false;
    }

    public bool Exists { get; }
    public bool IsDirectory { get; }
    public DateTimeOffset LastModified { get; }
    public long Length { get; }
    public string Name { get; }
    public string PhysicalPath { get; }

    public Stream CreateReadStream()
    {
        return new MemoryStream(_fileContent);
    }
}

第二步:实现内存目录内容类

创建InMemoryDirectoryContents类实现IDirectoryContents接口,用于返回目录下的所有内存文件:

using Microsoft.Extensions.FileProviders;
using System.Collections;
using System.Collections.Generic;

public class InMemoryDirectoryContents : IDirectoryContents
{
    private readonly IEnumerable<IFileInfo> _fileInfos;

    public InMemoryDirectoryContents(IEnumerable<IFileInfo> fileInfos)
    {
        _fileInfos = fileInfos;
        Exists = true;
    }

    public bool Exists { get; }

    public IEnumerator<IFileInfo> GetEnumerator()
    {
        return _fileInfos.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

第三步:实现内存文件提供程序

创建InMemoryFileProvider类实现IFileProvider接口,管理内存中的文件集合:

using Microsoft.Extensions.FileProviders;
using System;
using System.Collections.Generic;
using System.Linq;

public class InMemoryFileProvider : IFileProvider
{
    private readonly Dictionary<string, IFileInfo> _files = new Dictionary<string, IFileInfo>(StringComparer.OrdinalIgnoreCase);

    public void AddFile(string path, byte[] content)
    {
        var fileName = Path.GetFileName(path);
        _files[path] = new InMemoryFileInfo(fileName, content);
    }

    public IFileInfo GetFileInfo(string subpath)
    {
        if (_files.TryGetValue(subpath, out var fileInfo))
        {
            return fileInfo;
        }
        return new NotFoundFileInfo(subpath);
    }

    public IDirectoryContents GetDirectoryContents(string subpath)
    {
        var files = _files.Values.Where(f => Path.GetDirectoryName(subpath) == Path.GetDirectoryName(f.Name));
        return new InMemoryDirectoryContents(files);
    }

    public IChangeToken Watch(string filter)
    {
        return NullChangeToken.Singleton;
    }

    private class NotFoundFileInfo : IFileInfo
    {
        public NotFoundFileInfo(string path)
        {
            Name = path;
        }

        public bool Exists => false;
        public bool IsDirectory => false;
        public DateTimeOffset LastModified => DateTimeOffset.MinValue;
        public long Length => -1;
        public string Name { get; }
        public string PhysicalPath => null;

        public Stream CreateReadStream()
        {
            throw new FileNotFoundException($"文件 {Name} 不存在");
        }
    }
}

第四步:在ASP.NET Core中注册使用

Program.cs中注册内存文件提供程序,并添加静态文件中间件:

var builder = WebApplication.CreateBuilder(args);

// 创建内存文件提供程序并添加测试文件
var inMemoryFileProvider = new InMemoryFileProvider();
inMemoryFileProvider.AddFile("/test.txt", System.Text.Encoding.UTF8.GetBytes("这是内存中的测试文件内容"));

// 将内存文件提供程序添加到文件提供程序集合
builder.Services.AddSingleton<IFileProvider>(inMemoryFileProvider);

var app = builder.Build();

// 配置静态文件中间件使用自定义文件提供程序
app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = inMemoryFileProvider
});

app.MapGet("/", () => "虚拟文件提供程序示例,访问 /test.txt 可查看内存中的文件");

app.Run();

实现数据库虚拟文件提供程序

第一步:创建数据库文件实体

假设使用EF Core存储文件,首先创建DbFile实体类:

public class DbFile
{
    public int Id { get; set; }
    public string FilePath { get; set; }
    public string FileName { get; set; }
    public byte[] FileContent { get; set; }
    public DateTimeOffset UploadTime { get; set; }
}

第二步:实现数据库文件信息类

创建DbFileInfo类实现IFileInfo接口:

using Microsoft.Extensions.FileProviders;
using System.IO;

public class DbFileInfo : IFileInfo
{
    private readonly byte[] _fileContent;

    public DbFileInfo(DbFile dbFile)
    {
        Name = dbFile.FileName;
        _fileContent = dbFile.FileContent;
        Exists = true;
        Length = dbFile.FileContent.Length;
        LastModified = dbFile.UploadTime;
        PhysicalPath = null;
        IsDirectory = false;
    }

    public bool Exists { get; }
    public bool IsDirectory { get; }
    public DateTimeOffset LastModified { get; }
    public long Length { get; }
    public string Name { get; }
    public string PhysicalPath { get; }

    public Stream CreateReadStream()
    {
        return new MemoryStream(_fileContent);
    }
}

第三步:实现数据库文件提供程序

创建DbFileProvider类实现IFileProvider接口,从数据库查询文件信息:

using Microsoft.Extensions.FileProviders;
using Microsoft.EntityFrameworkCore;
using System;
using System.Linq;

public class DbFileProvider : IFileProvider
{
    private readonly AppDbContext _dbContext;

    public DbFileProvider(AppDbContext dbContext)
    {
        _dbContext = dbContext;
    }

    public IFileInfo GetFileInfo(string subpath)
    {
        var dbFile = _dbContext.DbFiles.FirstOrDefault(f => f.FilePath == subpath);
        if (dbFile != null)
        {
            return new DbFileInfo(dbFile);
        }
        return new NotFoundFileInfo(subpath);
    }

    public IDirectoryContents GetDirectoryContents(string subpath)
    {
        var files = _dbContext.DbFiles
            .Where(f => f.FilePath.StartsWith(subpath))
            .Select(f => new DbFileInfo(f))
            .ToList();
        return new InMemoryDirectoryContents(files);
    }

    public IChangeToken Watch(string filter)
    {
        return NullChangeToken.Singleton;
    }

    private class NotFoundFileInfo : IFileInfo
    {
        public NotFoundFileInfo(string path)
        {
            Name = path;
        }

        public bool Exists => false;
        public bool IsDirectory => false;
        public DateTimeOffset LastModified => DateTimeOffset.MinValue;
        public long Length => -1;
        public string Name { get; }
        public string PhysicalPath => null;

        public Stream CreateReadStream()
        {
            throw new FileNotFoundException($"文件 {Name} 不存在");
        }
    }
}

第四步:注册数据库文件提供程序

Program.cs中配置数据库上下文和文件提供程序:

var builder = WebApplication.CreateBuilder(args);

// 配置数据库上下文
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlite("Data Source=files.db"));

// 注册数据库文件提供程序
builder.Services.AddScoped<IFileProvider, DbFileProvider>();

var app = builder.Build();

// 初始化数据库并添加测试文件
using (var scope = app.Services.CreateScope())
{
    var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
    dbContext.Database.EnsureCreated();
    if (!dbContext.DbFiles.Any())
    {
        dbContext.DbFiles.Add(new DbFile
        {
            FilePath = "/db_test.txt",
            FileName = "db_test.txt",
            FileContent = System.Text.Encoding.UTF8.GetBytes("这是数据库中的测试文件内容"),
            UploadTime = DateTimeOffset.UtcNow
        });
        dbContext.SaveChanges();
    }
}

app.UseStaticFiles(new StaticFileOptions
{
    FileProvider = app.Services.GetRequiredService<IFileProvider>()
});

app.MapGet("/", () => "数据库虚拟文件提供程序示例,访问 /db_test.txt 可查看数据库中的文件");

app.Run();

注意事项

  • 虚拟文件提供程序的路径匹配区分大小写,建议统一使用小写路径避免问题
  • 数据库文件提供程序中,查询文件时建议添加索引提升查询效率
  • 如果文件较大,需要注意内存占用,可考虑分块读取流的方式优化
  • 生产环境中如果需要监听文件变化,可以实现IChangeToken接口返回对应的变更令牌

ASP.NET_CoreVirtualFileProviderIFileProviderC#修改时间:2026-06-09 17:24:39

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。