VirtualFileSystem
物理的文件有wwwroot下文件(JS\CSS\html居多),DLL嵌入的资源(json文件居多),Razor文件,
如何操作这些文件呢,首先先表示这个文件抽象(IFileInfo )
IFileInfo 表示给定文件提供程序中的某个文件。它是文件一个抽象(名称、物理路径,是否存在,IsDirectory:如果 TryGetDirectoryContents 枚举了子目录,则为 True。 ,返回只读流文件string,bytes),
其次,提供文件的操作,IFileProvider,得到目录内容(IFileInfo列表,如果有),得到文件IFileInfo,以及监视
针对以上文件,提供有physicalFileProvider(磁盘)、EmbeddedFileProvider(程序集中)、CompositeFileProvider(IFileProvider 的集合)
针对这些ABP创建了IVirtualFileProvider,它还是基于CompositeFileProvider进行IFileProvider遍历,因此存在优先级。Dymanic>physical?Embedded
下面创建CompositeFileProvider,它由下面组成
dynamicFileProvider:自创建的文件,基于InMemoryFileInfo
PhysicalFileProvider:物理文件,由配置文件AbpVirtualFileSystemOptions导入,如果PhysicalPath存在
InternalVirtualFileProvider:嵌入的文件,基于DictionaryBasedFileProvider,由配置文件AbpVirtualFileSystemOptions导入
protected virtual IFileProvider CreateHybridProvider(IDynamicFileProvider dynamicFileProvider) { var fileProviders = new List<IFileProvider>(); fileProviders.Add(dynamicFileProvider); if (_options.FileSets.PhysicalPaths.Any()) { fileProviders.AddRange( _options.FileSets.PhysicalPaths .Select(rootPath => new PhysicalFileProvider(rootPath)) .Reverse() ); } fileProviders.Add(new InternalVirtualFileProvider(_options)); return new CompositeFileProvider(fileProviders); }
回到配置文件AbpVirtualFileSystemOptions的FileSets列表,其使用的拓展方法有
AddEmbedded:引入Assembly,注意baseNamespace
list.Add( new EmbeddedFileSet( typeof(T).Assembly, baseNamespace, baseFolderInProject ) );
ReplaceEmbeddedByPhysical:
我们需要的是应用程序在开发时直接使用物理文件的能力, 让浏览器刷新时同步JavaScript文件的任何更改. ReplaceEmbeddedByPhysical
方法使其成为可能.
分析基于IFileProvider的IVirtualFileProvider实现方法,它的基类型是DictionaryBasedFileProvider,它实现基于IDictionary<string, IFileInfo> Files
它在Options的Add方法EmbeddedFileSet,调用AddFiles方法,它去除BaseNamespace,目的是填充了IDictionary字典
foreach (var resourcePath in Assembly.GetManifestResourceNames()) { if (!BaseNamespace.IsNullOrEmpty() && !resourcePath.StartsWith(BaseNamespace)) { continue; } //上面BaseNamespace不为空或与BaseNamespace开头任一条件都执行以下 //去掉BaseNamespace.开始部分,split('.')成数组,后两项是文件名,前面是文件夹,join起来成完全的路径 var fullPath = ConvertToRelativePath(resourcePath).EnsureStartsWith('/'); //如果包括/,说明有目录,则添加目录VirtualDirectoryFileInfo,然后递归,逐层去掉/,直到没有/ if (fullPath.Contains("/")) { AddDirectoriesRecursively(files, fullPath.Substring(0, fullPath.LastIndexOf('/')), lastModificationTime); } //添加文件EmbeddedResourceFileInfo files[fullPath] = new EmbeddedResourceFileInfo( Assembly, resourcePath, fullPath, CalculateFileName(fullPath), lastModificationTime ); }
GetFileInfo方法,输入不能为空的,则根据字典GetOrDefault(路径),它可以得到文件EmbeddedResourceFileInfo,也可以得到VirtualDirectoryFileInfo
GetDirectoryContents,它是基于GetFileInfo得到,输入也不能为空的,如果到是文件名,则直接返回没有找到目录,否则遍历字典的File,它去除输入目录,
找到路径不包含/,注意它可能是文件名,也可能是目录
foreach (var fileInfo in Files.Values) { var fullPath = fileInfo.GetVirtualOrPhysicalPathOrNull(); if (!fullPath.StartsWith(directoryPath)) { continue; } var relativePath = fullPath.Substring(directoryPath.Length); if (relativePath.Contains("/")) { continue; } fileList.Add(fileInfo); }
WebContentFileProvider实现
它是PhysicalFileProvider,以及_virtualFileProvider的复合FileProvider
protected virtual IFileProvider CreateFileProvider() { return new CompositeFileProvider( new PhysicalFileProvider(_hostingEnvironment.ContentRootPath), _virtualFileProvider ); }
1)GetFileInfo方法,要求以/Pages、/Views、/Themes开始,并且,拓展名以.js,.css,.png。.jpg,.jpeg结束等文件
否则要加上/wwwroot
2) 同理是GetDirectoryContents,以以/Pages、/Views、/Themes开始
1、静态资源,使用MiddleWare
public static IApplicationBuilder UseVirtualFiles(this IApplicationBuilder app) { return app.UseStaticFiles( new StaticFileOptions { FileProvider = app.ApplicationServices.GetRequiredService<IWebContentFileProvider>() } ); }
2、RazorEngine
//Configure Razor context.Services.Insert(0, ServiceDescriptor.Singleton<IConfigureOptions<MvcRazorRuntimeCompilationOptions>>( new ConfigureOptions<MvcRazorRuntimeCompilationOptions>(options => { options.FileProviders.Add( new RazorViewEngineVirtualFileProvider( context.Services.GetSingletonInstance<IObjectAccessor<IServiceProvider>>() ) ); } ) ) );
模块内添加嵌入式资源
1、AbpLocalizationModule下面有AbpValidation
2、
Configure<AbpVirtualFileSystemOptions>(options => { options.FileSets.AddEmbedded<AbpAspNetCoreMvcUiBootstrapModule>("Volo.Abp.AspNetCore.Mvc.UI.Bootstrap"); }); Configure<AbpVirtualFileSystemOptions>(options => { options.FileSets.AddEmbedded<AbpAspNetCoreMvcUiMultiTenancyModule>(); }); Configure<AbpVirtualFileSystemOptions>(options => { options.FileSets.AddEmbedded<AbpAspNetCoreMvcUiBasicThemeModule>("Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic"); }); public override void ConfigureServices(ServiceConfigurationContext context) { Configure<AbpVirtualFileSystemOptions>(options => { options.FileSets.AddEmbedded<AbpEmailingModule>(); }); } Configure<AbpVirtualFileSystemOptions>(options => { options.FileSets.AddEmbedded<AbpAspNetCoreMvcUiWidgetsModule>(); }); Configure<AbpVirtualFileSystemOptions>(options => { options.FileSets.AddEmbedded<AbpLocalizationModule>("Volo.Abp", "Volo/Abp"); }); Configure<AbpVirtualFileSystemOptions>(options => { options.FileSets.AddEmbedded<AbpUiNavigationModule>(); }); Configure<AbpVirtualFileSystemOptions>(options => { options.FileSets.AddEmbedded<AbpUiNavigationModule>(); }); Configure<AbpVirtualFileSystemOptions>(options => { options.FileSets.AddEmbedded<AbpUiModule>(); });