重新整理 .net core 实践篇—————文件系统[二十二]
前言
简单介绍一下文件系统。
正文
文件系统,主要是下面3个接口组成:
-
IFileProvider
-
IFileInfo
-
IDirectoryContents
那么他们的实现是:
-
physicalFileProvider 物理文件提供程序
-
enbeddedFileProvider 嵌入式文件提供程序
-
compositeFileProvider 组合文件提供程序
/// <summary>A read-only file provider abstraction.</summary>
public interface IFileProvider
{
/// <summary>Locate a file at the given path.</summary>
/// <param name="subpath">Relative path that identifies the file.</param>
/// <returns>The file information. Caller must check Exists property.</returns>
IFileInfo GetFileInfo(string subpath);
/// <summary>Enumerate a directory at the given path, if any.</summary>
/// <param name="subpath">Relative path that identifies the directory.</param>
/// <returns>Returns the contents of the directory.</returns>
IDirectoryContents GetDirectoryContents(string subpath);
/// <summary>
/// Creates a <see cref="T:Microsoft.Extensions.Primitives.IChangeToken" /> for the specified <paramref name="filter" />.
/// </summary>
/// <param name="filter">Filter string used to determine what files or folders to monitor. Example: **/*.cs, *.*, subFolder/**/*.cshtml.</param>
/// <returns>An <see cref="T:Microsoft.Extensions.Primitives.IChangeToken" /> that is notified when a file matching <paramref name="filter" /> is added, modified or deleted.</returns>
IChangeToken Watch(string filter);
}
IFileProvider A read-only file provider abstraction
只读文件提供程序抽象。
那么这个三个方法分别是:
-
GetFileInfo 获取指定文件
-
GetDirectoryContents 获取指定目录,subpath是相对路径
看下其返回值IDirectoryContents:
public interface IDirectoryContents : IEnumerable<IFileInfo>, IEnumerable
{
/// <summary>True if a directory was located at the given path.</summary>
bool Exists { get; }
}
其继承IEnumerable
IFileInfo 可以获取的信息如下:
public interface IFileInfo
{
/// <summary>
/// True if resource exists in the underlying storage system.
/// </summary>
bool Exists { get; }
/// <summary>
/// The length of the file in bytes, or -1 for a directory or non-existing files.
/// </summary>
long Length { get; }
/// <summary>
/// The path to the file, including the file name. Return null if the file is not directly accessible.
/// </summary>
string PhysicalPath { get; }
/// <summary>
/// The name of the file or directory, not including any path.
/// </summary>
string Name { get; }
/// <summary>When the file was last modified</summary>
DateTimeOffset LastModified { get; }
/// <summary>
/// True for the case TryGetDirectoryContents has enumerated a sub-directory
/// </summary>
bool IsDirectory { get; }
/// <summary>
/// Return file contents as readonly stream. Caller should dispose stream when complete.
/// </summary>
/// <returns>The file stream</returns>
Stream CreateReadStream();
}
这里面有一个IsDirectory 属性,看来目录也是当成了文件的,这样设计可能是兼容linux的原因吧,一切皆文件。
- 第三个 Watch,看到IChangeToken 就知道肯定是搞监听的。
测试:
static void Main(string[] args)
{
IFileProvider provider = new PhysicalFileProvider(AppDomain.CurrentDomain.BaseDirectory);
var contents = provider.GetDirectoryContents("/");
foreach (var item in contents)
{
Console.WriteLine(item.Name);
}
Console.ReadLine();
}
打印根目录全部文件。
查看内嵌文件:
在根目录创建emb.html,设置为内嵌。
里面为:
<!DOCTYPE html>
<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta charset="utf-8" />
<title></title>
</head>
<body>
这个是一个内嵌文件
</body>
</html>
测试代码:
IFileProvider provider = new EmbeddedFileProvider(typeof(Program).Assembly);
var html = provider.GetFileInfo("emb.html");
Console.ReadLine();
结果:
下面看下组合文件提供程序:
static void Main(string[] args)
{
IFileProvider provider1 = new PhysicalFileProvider(AppDomain.CurrentDomain.BaseDirectory);
IFileProvider provider2 = new EmbeddedFileProvider(typeof(Program).Assembly);
IFileProvider providerSum = new CompositeFileProvider(provider1, provider2);
var contents = providerSum.GetDirectoryContents("/");
foreach (var item in contents)
{
Console.WriteLine(item.Name);
}
Console.ReadLine();
}
简单看下这个组合是如何实现的。
public CompositeFileProvider(params IFileProvider[] fileProviders)
{
this._fileProviders = fileProviders ?? Array.Empty<IFileProvider>();
}
先是放置在一个数组中,名为_fileProviders 。
看下GetDirectoryContents:
public IDirectoryContents GetDirectoryContents(string subpath)
{
return (IDirectoryContents) new CompositeDirectoryContents((IList<IFileProvider>) this._fileProviders, subpath);
}
看下CompositeDirectoryContents:
public CompositeDirectoryContents(IList<IFileProvider> fileProviders, string subpath)
{
if (fileProviders == null)
throw new ArgumentNullException(nameof (fileProviders));
this._fileProviders = fileProviders;
this._subPath = subpath;
}
然后遍历的时候调用:
/// <summary>Creates an enumerator for all files in all providers given.
/// Ensures each item in the collection is distinct.</summary>
/// <returns>An enumerator over all files in all given providers</returns>
public IEnumerator<IFileInfo> GetEnumerator()
{
this.EnsureFilesAreInitialized();
return (IEnumerator<IFileInfo>) this._files.GetEnumerator();
}
查看EnsureFilesAreInitialized:
private void EnsureFilesAreInitialized()
{
this.EnsureDirectoriesAreInitialized();
if (this._files != null)
return;
this._files = new List<IFileInfo>();
HashSet<string> stringSet = new HashSet<string>();
for (int index = 0; index < this._directories.Count; ++index)
{
foreach (IFileInfo fileInfo in (IEnumerable<IFileInfo>) this._directories[index])
{
if (stringSet.Add(fileInfo.Name))
this._files.Add(fileInfo);
}
}
}
继续查看EnsureDirectoriesAreInitialized:
private void EnsureDirectoriesAreInitialized()
{
if (this._directories != null)
return;
this._directories = new List<IDirectoryContents>();
foreach (IFileProvider fileProvider in (IEnumerable<IFileProvider>) this._fileProviders)
{
IDirectoryContents directoryContents = fileProvider.GetDirectoryContents(this._subPath);
if (directoryContents != null && directoryContents.Exists)
{
this._exists = true;
this._directories.Add(directoryContents);
}
}
}
这样看来就是每隔文件的provider 调用GetDirectoryContents,然后再次遍历套娃把文件信息都放入List
功能挺少的,这样看来必须是同一个subpath,只能在外面根目录做文章。
结
以上只是个人整理,如有错误,望请指点。
下一节路由与终结点。