• 如何在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

  • 相关阅读:
    在模板生成页面的时候,页面里的标签可能会生成多个id,这时候使用id选择器,往往只能取到第一个id的元素。
    后台返回model里的时间格式,用@JsonFormat是没用的,它只有在返回JSON数据的时候生效,我脑抽了
    thymeleaf关于 Error resolving template “index”, template might not exist or might not be accessible by any of the configured Template Resolvers
    thymeleaf 配合 Spring Security 权限判断时,sec:authentication无法取到值(null)
    MySQL 常用30种SQL查询语句优化方法
    Linux常用命令
    APP微信登录---第三方登录
    关于文件的工具类例子
    Java时间戳与日期格式字符串的互转
    Java字符串与文件的互转操作
  • 原文地址:https://www.cnblogs.com/luomingui/p/12573309.html
Copyright © 2020-2023  润新知