如何在ASP.NET Core中自定义Azure Storage File Provider

文章标题:如何在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接口

Copy

	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接口。

Copy

	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方法的实现了

Copy

	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

Copy

	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对象, 这个对象将作为NamePhysicalPathLastModified等属性的数据源。
  • 我们使用CloudFile对象DownloadRangeToStreamAsync, 将其对应的文件流下载。注意这里加载文件流之后,需要将文件流的Position置0(即流的头部)
  • 文件的长度即文件流的长度
  • 强制设置IsDirectory属性为false, 因为当前处理的是文件

AzureDirectoryInfo

Copy

	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对象, 这个对象将作为NamePhysicalPathLastModified等属性的数据源。
  • 强制设置IsDirectory属性为true, 因为当前处理的是目录
  • 这里我们没有实现Length属性和CreateReadStream, 因为我们处理的是目录, 这2个属性没有必要实现。

实现GetFileInfo方法#

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

Copy

	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的配置

例:

Copy

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

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

Copy

	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

原文地址:https://www.cnblogs.com/luomingui/p/12573309.html

时间: 2025-01-11 12:45:50

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

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

注:本文提到的代码示例下载地址> How to using Entity Framework DB first in ASP.NET Core 如何在ASP.NET Core中应用Entity Framework 首先为大家提醒一点,.NET Core和经典.NET Framework的Library是不通用的,包括Entity Framework! 哪怎么办? 别急,微软为.NET Core发布了.NET Core版本的Entity Framework,具体配置方法与经典.NET Framew

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

注:本文提到的代码示例下载地址> How to achieve a basic authorization in ASP.NET Core 如何在ASP.NET Core中实现一个基础的身份认证 ASP.NET终于可以跨平台了,但是不是我们常用的ASP.NET, 而是叫一个ASP.NET Core的新平台,他可以跨Windows, Linux, OS X等平台来部署你的web应用程序,你可以理解为,这个框架就是ASP.NET的下一个版本,相对于传统ASP.NET程序,它还是有一些不同的地方的,比

如何在ASP.NET Core 中快速构建PDF文档

比如我们需要ASP.NET Core 中需要通过PDF来进行某些简单的报表开发,随着这并不难,但还是会手忙脚乱的去搜索一些资料,那么恭喜您,这篇帖子会帮助到您,我们就不会再去浪费一些宝贵的时间. 在本文中我们将要使用DinkToPDF来处理我们在.NET Core Web 程序中进行构建PDF文档!就现在我们不多说,直接开始有趣的部分. 前言 您可以通过创建PDF文档在我的仓库中,获取源代码,欢迎给个免费的Star... 现在我们创建一个.NET Core 3.0 项目,至于是mvc.Api.这

如何在ASP.NET Core中使用Redis

注:本文提到的代码示例下载地址> https://code.msdn.microsoft.com/How-to-use-Redis-in-ASPNET-0d826418 Redis是一个开源的内存中的数据结构存储系统,可以用作数据库.缓存和消息中间件.它支持多种类型的数据结构:字符串,哈希表,列表,集合,有序集等等. Redis 官方没有推出Windows版本,倒是由Microsoft Open Tech提供了Windows 64bit 版本支持. 如何在Windows机器上安装Redis=>

ASP.NET Core中Middleware的使用

ASP.NET 5中Middleware的基本用法 在ASP.NET 5里面引入了OWIN的概念,大致意思是将网站部署.服务器.中间组件以及应用分离开,这里提到的Middleware就是中间组件. 这里引用asp.net网站的介绍图 Middleware的作用有点类似于httpmodule,服务器接收到的请求都会传递到各个Middleware,多个Middleware形成一个处理管道. 由于不针对于特定的请求,所以Middleware的执行范围是在Application之外,这种模式很适合处理日

ASP.NET Core中使用xUnit进行单元测试

单元测试的功能自从MVC的第一个版本诞生的时候,就是作为一个重要的卖点来介绍的,通常在拿MVC与webform比较的时候,单元测试就是必杀底牌,把webform碾压得一无是处. 单元测试的重要性不用多说了,有单元测试的做兜底的项目,好比给开发人员买了份保险,当然这个保险的质量取决于单元测试的质量,那些一路Mock的单元测试,看起来很美,但是什么都cover不到.目前工作中的一个老项目,有2万多个单元测试用例,其中不少是用心之作,真正落实到了业务逻辑,开发人员可以放心的去修改代码,当然一切都必须按

[译]ASP.NET Core中使用MediatR实现命令和中介者模式

作者:依乐祝 原文地址:https://www.cnblogs.com/yilezhu/p/9866068.html 在本文中,我将解释命令模式,以及如何利用基于命令模式的第三方库来实现它们,以及如何在ASP.NET Core中使用它来解决我们的问题并使代码简洁.因此,我们将通过下面的主题来进行相关的讲解. 什么是命令模式? 命令模式的简单实例以及中介者模式的简单描述 MVC中的瘦控制器是什么?我们是如如何实现使控制器变瘦的? 我们如何在我们的.NET Core应用程序中使用MediatR 使用

在Asp.Net Core中集成Kafka(中)

在上一篇中我们主要介绍如何在Asp.Net Core中同步Kafka消息,通过上一篇的操作我们发现上面一篇中介绍的只能够进行简单的首发kafka消息并不能够消息重发.重复消费.乐观锁冲突等问题,这些问题在实际的生产环境中是非常要命的,如果在消息的消费方没有做好必须的幂等性操作,那么消费者重复消费的问题会比较严重的,另外对于消息的生产者来说,记录日志的方式也不是足够友好,很多时候在后台监控程序中我们需要知道记录更多的关于消息的分区.偏移等更多的消息.而在消费者这边我们更多的需要去解决发送方发送重复

ASP.NET Core中显示自定义错误页面

在 ASP.NET Core 中,默认情况下当发生500或404错误时,只返回http状态码,不返回任何内容,页面一片空白. 如果在 Startup.cs 的 Configure() 中加上 app.UseStatusCodePages(); ,500错误时依然是一片空白(不知为何对500错误不起作用),404错误时有所改观,页面会显示下面的文字: Status Code: 404; Not Found 如果我们想不管500还是404错误都显示友好的自定义错误页面,该如何实现呢?请看下面的分解.