文章标题:如何在ASP.NET Core中自定义Azure Storage File Provider

作者:Lamond Lu

地址:https://www.cnblogs.com/lwqlun/p/10406566.html

项目源代码: https://github.com/lamondlu/AzureFileProvider

背景

ASP.NET Core是一个扩展性非常高的框架,开发人员可以根据自己的需求扩展出想要的功能。File Provider是ASP.NET Core中的一个重要组件,通过这个组件,开发人员可以暴露一组文件,并允许应用程序像访问静态文件一样访问暴露的文件。

ASP.NET Core中内置了3种File Provider

  • PhysicalFileProvider - 用来访问和应用程序部署在一起的静态文件
  • ManifestEmbeddedFileProvider - 用来访问程序集中的内嵌文件
  • CompositeFileProvider - 将多个File Provider合并使用

那么如何自定义一个File Provider呢?比如如何将Azure Files Storage中的文件暴露给ASP.NET Core应用程序。今天我们来演示一下,如果通过实现IFileProvider 接口来实现一个Azure Files Storage Provider。

本文中只针对Azure Files Storage, Azure Blob Storage的实现可以参见Filip w的博文

创建.NET Core Library项目

首先我们使用Visual Studio 2017,创建一个Class Library项目, 命名为AzureFileProvider

为了使用IFileProvider接口和Azure Storage服务,这里我们需要使用Nuget引入2个库

  • Microsoft.AspNetCore.App
  • WindowsAzure.Storage

创建AzureFileProvider

为了创建一个ASP.NET Core支持的File Provider, 我们就需要自己创建一个类,并让它实现IFileProvider接口。

这里首先我们创建一个类AzureFileProvider, 它实现了IFileProvider接口

	public class AzureFileProvider : IFileProvider
{
public IDirectoryContents GetDirectoryContents(string subpath)
{
throw new NotImplementedException();
} public IFileInfo GetFileInfo(string subpath)
{
throw new NotImplementedException();
} public IChangeToken Watch(string filter)
{
throw new NotImplementedException();
}
}

从以上代码中,我们可以了解到,IFileProvider接口定义了3个需要实现方法

  • GetDirectoryContents - 这个方法是用来获取指定目录下的内容的
  • GetFileInfo - 这个方法使用来获取指定文件内容的
  • Watch - 这个方法是用来监听文件变更的,这个暂时不需要实现它

实现GetDirectoryContents方法

为了实现GetDirectoryContents方法,我们需要首先创建一个IDirectoryContents接口的实现类, 因为它是这个方法的返回类型。

我们创建一个类AzureStorageDirectoryContents, 它实现了IDirectoryContents接口。

	public class AzureStorageDirectoryContents : IDirectoryContents
{
private List<IFileInfo> _listInfos; public AzureStorageDirectoryContents(List<IFileInfo> listInfos)
{
_listInfos = listInfos;
} public bool Exists
{
get
{
return true;
}
} public IEnumerator<IFileInfo> GetEnumerator()
{
return _listInfos.GetEnumerator();
} IEnumerator IEnumerable.GetEnumerator()
{
return _listInfos.GetEnumerator();
}
}

代码解释:

  • 这里IDirectoryContents其实就是为了显示指定目录中的文件结构
  • IFileInfo接口对象既可以表示文件也可以表示子目录,这个接口的2个实现我会在后面说明
  • 这里我们通过构造函数,将指定文件夹内的文件结构注入到了AzureStorageDirectoryContents雷中。

下面我们就可以来添加GetDirectoryContents方法的实现了

	private AzureStorageSetting _setting = null;

    public AzureFileProvider(AzureStorageSetting setting)
{
_setting = setting;
} public IDirectoryContents GetDirectoryContents(string subpath)
{
var rootDirectory = GetRootDirectory(); var folderName = subpath.Substring(1);
CloudFileDirectory folder = null; if (string.IsNullOrWhiteSpace(folderName))
{
folder = rootDirectory;
}
else
{
folder = rootDirectory.GetDirectoryReference(folderName);
} var files = new List<IFileInfo>();
foreach (var item in folder
.ListFilesAndDirectoriesSegmentedAsync(new FileContinuationToken())
.Result
.Results)
{
if (item is CloudFile)
{
var file = item as CloudFile;
files.Add(new AzureFileInfo(file));
}
else if (item is CloudFileDirectory)
{
var directory = item as CloudFileDirectory;
files.Add(new AzureDirectoryInfo(directory));
}
} return new AzureStorageDirectoryContents(files);
} private CloudFileDirectory GetRootDirectory()
{
var shareName = _setting.ShareName;
var storageAccount = CloudStorageAccount.Parse(_setting.ConnectionString);
var fileClient = storageAccount.CreateCloudFileClient();
var share = fileClient.GetShareReference(shareName);
var rootDir = share.GetRootDirectoryReference(); return rootDir;
}

代码解释:

  • 这里我们通过构造函数为AzureFileProvider类注入了一个Azure Files Storage强类型配置类AzureStorageSetting, 它的数据源是appSettings.json, 后续我们会通过强类型配置将其注入
  • GetRootDirectory方法是通过Azure Files Storage配置,获得Azure Files Storage中文件集合的根目录
  • GetDirectoryContentssubpath.Substring(1)代码的作用是去除subpath带的第一个“/”。如果不去除,会读取不到文件
  • 这里我们使用了ListFilesAndDirectoriesSegmentedAsync方法获取了指定目录中所有的文件和目录
  • 如果是文件,我们会实例化一个AzureFileInfo对象,如果是一个目录,我们会实例化一个AzureDirectoryInfo对象
  • 最终我们将读取到的所有文件和目录信息通过AzureStorageDirectoryContents类的构造函数注入。

创建AzureFileInfoAzureDirectoryInfo

为了区分文件和目录,我们创建2个新类AzureFileInfoAzureDirectoryInfo。 他们都实现了IFileInfo接口。

AzureFileInfo

	public class AzureFileInfo : IFileInfo
{
private CloudFile _file = null;
private MemoryStream _stream = new MemoryStream(); public AzureFileInfo(CloudFile file)
{
_file = file;
_file.DownloadRangeToStreamAsync(_stream, null, null).Wait();
_stream.Position = 0;
} public bool Exists
{
get
{
return true;
}
} public long Length
{
get
{
return _stream.Length;
}
} public string PhysicalPath
{
get
{
return _file.Uri.AbsolutePath;
}
} public string Name
{
get
{
return _file.Name;
}
} public DateTimeOffset LastModified
{
get
{
return _file.Properties.LastModified.GetValueOrDefault();
}
} public bool IsDirectory
{
get
{
return false;
}
} public Stream CreateReadStream()
{
return _stream;
}
}

代码解释

  • 这里我们通过AzureFileInfo的构造函数传入了一个CloudFile对象, 这个对象将作为Name, PhysicalPath, LastModified等属性的数据源。
  • 我们使用CloudFile对象DownloadRangeToStreamAsync, 将其对应的文件流下载。注意这里加载文件流之后,需要将文件流的Position置0(即流的头部)
  • 文件的长度即文件流的长度
  • 强制设置IsDirectory属性为false, 因为当前处理的是文件

AzureDirectoryInfo

	public class AzureDirectoryInfo : IFileInfo
{
private CloudFileDirectory _directory = null; public AzureDirectoryInfo(CloudFileDirectory directory)
{
_directory = directory;
} public bool Exists
{
get
{
return true;
}
} public long Length => throw new NotImplementedException(); public string PhysicalPath
{
get
{
return _directory.Uri.AbsolutePath;
}
} public string Name
{
get
{
return _directory.Name;
}
} public DateTimeOffset LastModified
{
get
{
return _directory.Properties.LastModified.GetValueOrDefault();
}
} public bool IsDirectory
{
get
{
return true;
}
} public Stream CreateReadStream()
{
throw new NotImplementedException();
}
}

代码解释

  • 这里我们通过AzureDirectoryInfo的构造函数传入了一个CloudFileDirectory对象, 这个对象将作为Name, PhysicalPath, LastModified等属性的数据源。
  • 强制设置IsDirectory属性为true, 因为当前处理的是目录
  • 这里我们没有实现Length属性和CreateReadStream, 因为我们处理的是目录, 这2个属性没有必要实现。

实现GetFileInfo方法

相对于GetDirectoryContents方法的实现,GetFileInfo方法就简单多了,我们只需要根据当前指定的subpath, 将文件信息返回即可。

	public IFileInfo GetFileInfo(string subpath)
{
var rootDirectory = GetRootDirectory();
var file = rootDirectory
.GetFileReference(subpath.Substring(1)); return new AzureFileInfo(file);
}

如何启用AzureFileProvider

下面我们来试验一下我们编写的AzureFileProvider是否能运行成功。

首先我们创建一个默认ASP.NET Core Api项目,并引用上一步中编译好的程序集AzureFileProvider.dll。

appSettings.json中, 我们需要定义Azure Files Storage的配置

例:

{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
},
"AzureStorage": {
"ConnectionString": "DefaultEndpointsProtocol=https;AccountName=fdsffsdf;AccountKey=fdsfsdfs;EndpointSuffix=core.windows.net",
"ShareName": "testShare"
}
}

第二步,我们需要修改Startup.cs文件的Configure方法。

	public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
AzureStorageSetting o = new AzureStorageSetting();
Configuration.Bind("AzureStorage", o); app.UseStaticFiles(new StaticFileOptions
{
FileProvider = new AzureFileProvider(o),
RequestPath = "/files"
}); app.UseDirectoryBrowser(new DirectoryBrowserOptions
{
FileProvider = new AzureFileProvider(o),
RequestPath = "/files"
}); app.UseMvc();
}

代码解释

  • 这里我们使用强类型配置绑定,获取了appSettings.json中的Azure Files Storage的配置
  • 在配置静态文件中间件部分,我们通过StaticFileOptions配置对象,指定了当前应用使用AzureFileProvider。
  • 为了演示效果,我这里也启用了DirectoryBrowser中间件,即可以使用网页查看目录结构。这个功能比较危险,在正式项目很少使用。所以正式使用时,最好将这段代码删掉。

最终效果

现在我们启动当前项目, 访问"/files", 即可查看到当前指定Azure Files Storage中的所有文件和目录

项目源代码

https://github.com/lamondlu/AzureFileProvider

Nuget程序集

以上类库,我已经发布到了Nuget上, 如果你不想每次都把前面的代码写一遍,可以直接安装这个程序集来使用。

Install-Package LamondLu.AzureFileProvider

如何在ASP.NET Core中自定义Azure Storage File Provider的更多相关文章

  1. 如何在ASP.NET Core中使用Azure Service Bus Queue

    原文:USING AZURE SERVICE BUS QUEUES WITH ASP.NET CORE SERVICES 作者:damienbod 译文:如何在ASP.NET Core中使用Azure ...

  2. 如何在ASP.NET Core中实现CORS跨域

    注:下载本文的完整代码示例请访问 > How to enable CORS(Cross-origin resource sharing) in ASP.NET Core 如何在ASP.NET C ...

  3. 如何在ASP.NET Core中实现一个基础的身份认证

    注:本文提到的代码示例下载地址> How to achieve a basic authorization in ASP.NET Core 如何在ASP.NET Core中实现一个基础的身份认证 ...

  4. 如何在ASP.NET Core中应用Entity Framework

    注:本文提到的代码示例下载地址> How to using Entity Framework DB first in ASP.NET Core 如何在ASP.NET Core中应用Entity ...

  5. [转]如何在ASP.NET Core中实现一个基础的身份认证

    本文转自:http://www.cnblogs.com/onecodeonescript/p/6015512.html 注:本文提到的代码示例下载地址> How to achieve a bas ...

  6. 如何在ASP.NET Core中使用JSON Patch

    原文: JSON Patch With ASP.NET Core 作者:.NET Core Tutorials 译文:如何在ASP.NET Core中使用JSON Patch 地址:https://w ...

  7. [翻译] 如何在 ASP.Net Core 中使用 Consul 来存储配置

    [翻译] 如何在 ASP.Net Core 中使用 Consul 来存储配置 原文: USING CONSUL FOR STORING THE CONFIGURATION IN ASP.NET COR ...

  8. [译]如何在ASP.NET Core中实现面向切面编程(AOP)

    原文地址:ASPECT ORIENTED PROGRAMMING USING PROXIES IN ASP.NET CORE 原文作者:ZANID HAYTAM 译文地址:如何在ASP.NET Cor ...

  9. 如何在 ASP.Net Core 中使用 Serilog

    记录日志的一个作用就是方便对应用程序进行跟踪和排错调查,在实际应用上都是引入 日志框架,但如果你的 日志文件 包含非结构化的数据,那么查询起来将是一个噩梦,所以需要在记录日志的时候采用结构化方式. 将 ...

随机推荐

  1. Java多线程:线程与进程

    实际上,线程和进程的区别,在学OS时必然是学习过的,所缺的不过是一些总结. 1. 进程 2. 线程 3. 进程与线程 4. 多进程与多线程对比 5. Java多进程与多线程 5.1. Java多进程 ...

  2. MySQL技术内幕 InnoDB存储引擎(笔记)

    1. InnoDB 体系架构 其中,后台程序主要负责刷新内存池中的数据,保证缓冲池中的内存缓存的是最近的数据. 此外将已经修改的数据刷新到磁盘文件,同时保证在数据库发生异常的时候Innodb能恢复正常 ...

  3. EF Core Model更新迁移

    EF Core 迁移 感觉就是以前EF Code First的自动同步数据库功能 内容:在你新增.更新TableModel后,如何自动化的更新DB中的真实Table.以及对这些更改进行一个版本控制. ...

  4. python_方法说明

    方法用来描述对象所具有的行为,例如,列表对象的追加元素.插入元素.删除原宿.排序,字符串对象的分隔.连接.排版.替换.烤箱的温度设置.烘烤,等等 在类中定义的方法可以粗略分为四大类:公有方法.私有方法 ...

  5. springboot: thymeleaf 使用详解

    springboot:thymeleaf,这篇文章将更加全面详细的介绍thymeleaf的使用.thymeleaf 是新一代的模板引擎,在spring4.0中推荐使用thymeleaf来做前端模版引擎 ...

  6. 关于ConcurrentSkipListMap的理解

    一.前言 JCIP 提到了在 Java 6 中引入了两个新的并发集合类 ConcurrentSkipListMap 和 ConcurrentSkipListSet.其实只要介绍一下 Concurren ...

  7. Oracle-10:分析函数

    ------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 分析函数: 分析函数,可以修改排序的规则!!!!!!!!!!!!!!!!!!!!!!!! 首先把数据库脚本给放 ...

  8. vue项目如何刷新当前页面

    1.场景 在处理列表时,常常有删除一条数据或者新增数据之后需要重新刷新当前页面的需求. 2.遇到的问题 1. 用vue-router重新路由到当前页面,页面是不进行刷新的 2.采用window.rel ...

  9. 【C#】对异步请求处理程序IHttpAsyncHandler的理解和分享一个易用性封装

    在asp.net项目中,添加一个[一般处理程序]来处理请求是很自然的事,这样会得到一个实现自IHttpHandler的类,然后只需在ProcessRequest方法中写上处理逻辑就行了.但是这样的一个 ...

  10. 11. 将博客部署到tomcat上

    springboot项目既可以以jar运行,也可以做成war包放到服务器上,因为我的博客项目涉及到文件上传,所以按照jar的方式就不可行,需要部署到tomcat上,具体做法如下:1. 修改pom.xm ...