文章标题:如何在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. TensorFlow-谷歌深度学习库 命令行参数

    程序的入口: tf.app.run tf.app.run( main=None, argv=None ) 运行程序,可以提供'main'函数以及函数参数列表.处理flag解析然后执行main函数. 什 ...

  2. unity3d从入门到精通要掌握什么内容

    Unity3d就业方向广.游戏行业占据了65%的比例,也有虚拟现实,增强现实等方向,就业前景火爆.可以从事的岗位:游戏开发工程师.移动应用开发工程师.游戏场景设计师.游戏特效设计师.VR开发工程师.A ...

  3. oracle数据库-错误编码大全

    ORA-00001: 违反唯一约束条件 (.) ORA-00017: 请求会话以设置跟踪事件   ORA-00018: 超出最大会话数   ORA-00019: 超出最大会话许可数   ORA-000 ...

  4. spring中jedis对redis的事务使用注意总结

    spring的@Transactional不支持redis的事务,并且redis的事务和其它关系型数据库的事务概念不是太一样,redis事务不支持回滚,并且一条命令出错后,后面的命令还会执行. 所以不 ...

  5. Python高级教程

    关键字is 和 == 的区别 a = 'hello world' b = 'hello world' a == b #返回True a is b #返回False 注意:is 判断是否是一个ID, = ...

  6. JavaScript-通过原型继承一个对象

    <script> //通过原型继承一个对象 //inherit()返回了一个继承原自原型对象P的属性的新对象 //這裡使用ECMAScript5中的object.create()函數(如果 ...

  7. 解决Ubuntu系统下的VMware Workstation无法打开虚拟网络编辑器界面的问题

    本文由荒原之梦原创,原文链接:http://zhaokaifeng.com/?p=630 操作环境: Ubuntu 17 VMware 14 pro for Linux 问题描述: 我在Ubuntu ...

  8. CKEditor、UBB编辑器的使用总结

    2018-04-08 22:07:16.188 ERROR 14728 --- [nio-8082-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Se ...

  9. linux系统安装mysql

    所有平台的Mysql下载地址为: MySQL 下载. 挑选你需要的 MySQL Community Server版本及对应的平台. 接下来我们在 Centos 系统下使用 yum 命令安装 MySql ...

  10. 大话RabbitMQ 基础入门

    ----------写在前面---------- 近些年微服务越来越火,让我也忍不住想去一窥微服务究竟,讲到微服务,就离不开分布式,而分布式,也离不开消息队列,在消息队列中,RabbitMQ可以说是比 ...