一个物理文件可以直接作为资源内嵌到编译生成的程序集中。借助于EmbeddedFileProvider,我们可以统一的编程方式来读取内嵌于某个程序集中的资源文件,不过在这之前我们必须知道如何将一个项目文件作为资源并嵌入到生成的程序集中。 [ 本文已经同步到《get='_blank'>ASP.NET Core框架揭秘》之中]
目录
定义2
1: {
2: ...
3: "buildOptions": {
4: ...
5: "embed": {
6: "builtIns": {
7: "include": "root/**/*.txt",
8: "exclude": "root/dir1/foobar/*.txt"
9: },
10: "includeFiles" : "root/dir1/foobar/foo.txt",
11: "excludeFiles" : "root/dir2/gux.txt"
12: }
13: }
14: }
定义3
1: {
2: ...
3: "buildOptions": {
4: ...
5: "embed": {
6: "builtIns": {
7: "include": "root/**/*.txt",
8: "exclude": "root/dir1/foobar/*.txt"
9: },
10: "includeFiles" : "root/dir1/foobar/foo.txt",
11: "excludeFiles" : "root/dir2/gux.txt"
12:
13: "mappings": {
14: "foo.txt": "root/dir1/foobar/foo.txt",
15: "baz.txt": "root/dir1/baz.txt"
16: }
17: }
18: }
19: }
除了将“buildOptions/embed”配置选项设置为上述这么一个对象之外,我们还具有一个更加简单的设置方式,那就是直接设置为一个Globbing Pattern表达式或者表达式数组。这样的设置相当于是将设置的Globbing Pattern表达式添加到incude列表中,所以如下所示的两种配置是完全等效的。
定义1
1: {
2: ...
3: "buildOptions": {
4: ...
5: "embed": {
6: "include" : ["root/**/foo.txt","root/**/bar.txt"]
7: }
8: }
9: }
定义2
1: {
2: ...
3: "buildOptions": {
4: ...
5: "embed" : ["root/**/foo.txt","root/**/bar.txt"]
6: }
7: }
8: }
表示程序集的Assembly对象定义了如下几个方法来提取内嵌资源的文件的相关信息和读取指定资源文件的内容。GetManifestResourceNames方法帮助我们获取记录在程序集清单文件中的资源文件名,而另一个方法GetManifestResourceInfo则获取指定资源文件的描述信息。如果我们需要读取某个资源文件的内容,我们可以将资源文件名称作为参数调用GetManifestResourceStream方法,该方**返回一个读取文件内容的输出流。
1: public abstract class Assembly
2: {
3: public virtual string[] GetManifestResourceNames();
4: public virtual ManifestResourceInfo GetManifestResourceInfo(string resourceName);
5: public virtual Stream GetManifestResourceStream(string name);
6: }
如上面的代码片段所示,我们在创建一个EmbeddedResourceFileInfo对象的时候需要指定内嵌资源文件在清单文件的中的名称(resourcePath)和所在的程序集,以及资源文件的“逻辑”名称(name)。由于一个EmbeddedResourceFileInfo对象总是对应着一个具体的内嵌资源文件,所以它的Exists属性返回True,IsDirectory属性返回False。由于资源文件系统并不具有层次还的目录结构,它所谓的物理路径毫无意义,所以PhysicalPath属性直接返回Null。CreateReadStream方法返回的是调用程序集的GetManifestResourceStream方法返回的输出流,而表示文件长度的Length返回的是这个Stream对象的长度。
如下所示的是 EmbeddedFileProvider的定义。当我们在创建一个EmbeddedFileProvider对象的时候,除了指定资源文件所在的程序集之外,还可以指定一个命名空间。对于由EmbeddedFileProvider构建的内嵌资源文件系统来说,文件的名称和这个命名空间共同组成资源文件在程序集清单中的文件名。同样以上图所示的这个项目为例,资源文件foo.txt在程序集清单中的文件名称为“App.root.dir1.foobar.foo.txt”,如果EmbeddedFileProvider采用的“App.root”作为命名空间,那么对应的资源文件在逻辑上的名称就应该是“dir1.foobar.foo.txt”,这就是我们在上面所谓的资源文件的逻辑名称。如果该命名空间没作显式设置,默认情况下会将程序集的名称“App”作为命名空间,那么这个资源文件的名称就应该是“root.dir1.foobar.foo.txt”。
1: public class EmbeddedFileProvider : IFileProvider
2: {
3: public EmbeddedFileProvider(Assembly assembly);
4: public EmbeddedFileProvider(Assembly assembly, string baseNamespace);
5:
6: public IDirectoryContents GetDirectoryContents(string subpath);
7: public IFileInfo GetFileInfo(string subpath);
8: public IChangeToken Watch(string pattern);
9: }
当我们指定资源文件的逻辑名称调用EmbeddedFileProvider的GetFileInfo方法时,该方**将它与命名空间一起组成资源文件在程序集清单的名称(路径分隔符会被替换成“.”)。如果对应的资源文件存在,那么一个EmbeddedResourceFileInfo会被创建并返回,否则返回的将是一个NotFoundFileInfo对象。对于内嵌资源文件系统来说,根本就不存在所谓的文件更新的问题,所以它的Watch方**返回一个HasChanged永远返回False的ChangeTokne对象。
由于 EmbeddedFileProvider构建的内嵌资源文件系统不存在层次化的目录结构,所有的资源文件可以视为统统存储在程序集的“根目录”下,所以它的GetDirectoryContents方法只有在我们指定一个空字符串或者“/”(空字符串和“/”都表示“根目录”)时才会返回一个描述这个“根目录”的DirectoryContents对象,该对象实际上是一组EmbeddedResourceFileInfo对象的集合。在其他情况下,EmbeddedFileProvider的GetDirectoryContents方法总是返回一个NotFoundDirectoryContents对象。
海外公司注册、海外银行开户、跨境平台代入驻、VAT、EPR等知识和在线办理:https://www.xlkjsw.com
原标题:.NET Core的文件系统[4]:由EmbeddedFileProvider构建的内嵌(资源)文件系统
关键词:.NET