netcore3.0 IFileProvider 文件系统

Nuget包:以Microsoft.Extensins.FileProviders开头的包中

Github地址:https://github.com/dotnet/extensions/tree/master/src/FileProviders

 

 

 一、PhysicalFileProvider

 /// <summary>
    /// Represents a file on a physical filesystem
    /// </summary>
    public class PhysicalFileInfo : IFileInfo
    {
        private readonly FileInfo _info;

        /// <summary>
        /// Initializes an instance of <see cref="PhysicalFileInfo"/> that wraps an instance of <see cref="System.IO.FileInfo"/>
        /// </summary>
        /// <param name="info">The <see cref="System.IO.FileInfo"/></param>
        public PhysicalFileInfo(FileInfo info)
        {
            _info = info;
        }

        /// <inheritdoc />
        public bool Exists => _info.Exists;

        /// <inheritdoc />
        public long Length => _info.Length;

        /// <inheritdoc />
        public string PhysicalPath => _info.FullName;

        /// <inheritdoc />
        public string Name => _info.Name;

        /// <inheritdoc />
        public DateTimeOffset LastModified => _info.LastWriteTimeUtc;

        /// <summary>
        /// Always false.
        /// </summary>
        public bool IsDirectory => false;

        /// <inheritdoc />
        public Stream CreateReadStream()
        {
            // We are setting buffer size to 1 to prevent FileStream from allocating it's internal buffer
            // 0 causes constructor to throw
            var bufferSize = 1;
            return new FileStream(
                PhysicalPath,
                FileMode.Open,
                FileAccess.Read,
                FileShare.ReadWrite,
                bufferSize,
                FileOptions.Asynchronous | FileOptions.SequentialScan);
        }
    }
/// <summary>
    /// Looks up files using the on-disk file system
    /// </summary>
    /// <remarks>
    /// When the environment variable "DOTNET_USE_POLLING_FILE_WATCHER" is set to "1" or "true", calls to
    /// <see cref="Watch(string)" /> will use <see cref="PollingFileChangeToken" />.
    /// </remarks>
    public class PhysicalFileProvider : IFileProvider, IDisposable
    {
        private const string PollingEnvironmentKey = "DOTNET_USE_POLLING_FILE_WATCHER";
        private static readonly char[] _pathSeparators = new[]
            {Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar};

        private readonly ExclusionFilters _filters;

        private readonly Func<PhysicalFilesWatcher> _fileWatcherFactory;
        private PhysicalFilesWatcher _fileWatcher;
        private bool _fileWatcherInitialized;
        private object _fileWatcherLock = new object();

        private bool? _usePollingFileWatcher;
        private bool? _useActivePolling;

        /// <summary>
        /// Initializes a new instance of a PhysicalFileProvider at the given root directory.
        /// </summary>
        /// <param name="root">The root directory. This should be an absolute path.</param>
        public PhysicalFileProvider(string root)
            : this(root, ExclusionFilters.Sensitive)
        {
        }

        /// <summary>
        /// Initializes a new instance of a PhysicalFileProvider at the given root directory.
        /// </summary>
        /// <param name="root">The root directory. This should be an absolute path.</param>
        /// <param name="filters">Specifies which files or directories are excluded.</param>
        public PhysicalFileProvider(string root, ExclusionFilters filters)
        {
            if (!Path.IsPathRooted(root))
            {
                throw new ArgumentException("The path must be absolute.", nameof(root));
            }

            var fullRoot = Path.GetFullPath(root);
            // When we do matches in GetFullPath, we want to only match full directory names.
            Root = PathUtils.EnsureTrailingSlash(fullRoot);
            if (!Directory.Exists(Root))
            {
                throw new DirectoryNotFoundException(Root);
            }

            _filters = filters;
            _fileWatcherFactory = () => CreateFileWatcher();
        }

        /// <summary>
        /// Gets or sets a value that determines if this instance of <see cref="PhysicalFileProvider"/>
        /// uses polling to determine file changes.
        /// <para>
        /// By default, <see cref="PhysicalFileProvider"/>  uses <see cref="FileSystemWatcher"/> to listen to file change events
        /// for <see cref="Watch(string)"/>. <see cref="FileSystemWatcher"/> is ineffective in some scenarios such as mounted drives.
        /// Polling is required to effectively watch for file changes.
        /// </para>
        /// <seealso cref="UseActivePolling"/>.
        /// </summary>
        /// <value>
        /// The default value of this property is determined by the value of environment variable named <c>DOTNET_USE_POLLING_FILE_WATCHER</c>.
        /// When <c>true</c> or <c>1</c>, this property defaults to <c>true</c>; otherwise false.
        /// </value>
        public bool UsePollingFileWatcher
        {
            get
            {
                if (_fileWatcher != null)
                {
                    throw new InvalidOperationException($"Cannot modify {nameof(UsePollingFileWatcher)} once file watcher has been initialized.");
                }

                if (_usePollingFileWatcher == null)
                {
                    ReadPollingEnvironmentVariables();
                }

                return _usePollingFileWatcher.Value;
            }
            set => _usePollingFileWatcher = value;
        }

        /// <summary>
        /// Gets or sets a value that determines if this instance of <see cref="PhysicalFileProvider"/>
        /// actively polls for file changes.
        /// <para>
        /// When <see langword="true"/>, <see cref="IChangeToken"/> returned by <see cref="Watch(string)"/> will actively poll for file changes
        /// (<see cref="IChangeToken.ActiveChangeCallbacks"/> will be <see langword="true"/>) instead of being passive.
        /// </para>
        /// <para>
        /// This property is only effective when <see cref="UsePollingFileWatcher"/> is set.
        /// </para>
        /// </summary>
        /// <value>
        /// The default value of this property is determined by the value of environment variable named <c>DOTNET_USE_POLLING_FILE_WATCHER</c>.
        /// When <c>true</c> or <c>1</c>, this property defaults to <c>true</c>; otherwise false.
        /// </value>
        public bool UseActivePolling
        {
            get
            {
                if (_useActivePolling == null)
                {
                    ReadPollingEnvironmentVariables();
                }

                return _useActivePolling.Value;
            }

            set => _useActivePolling = value;
        }

        internal PhysicalFilesWatcher FileWatcher
        {
            get
            {
                return LazyInitializer.EnsureInitialized(
                    ref _fileWatcher,
                    ref _fileWatcherInitialized,
                    ref _fileWatcherLock,
                    _fileWatcherFactory);
            }
            set
            {
                Debug.Assert(!_fileWatcherInitialized);

                _fileWatcherInitialized = true;
                _fileWatcher = value;
            }
        }

        internal PhysicalFilesWatcher CreateFileWatcher()
        {
            var root = PathUtils.EnsureTrailingSlash(Path.GetFullPath(Root));
            return new PhysicalFilesWatcher(root, new FileSystemWatcher(root), UsePollingFileWatcher, _filters)
            {
                UseActivePolling = UseActivePolling,
            };
        }

        private void ReadPollingEnvironmentVariables()
        {
            var environmentValue = Environment.GetEnvironmentVariable(PollingEnvironmentKey);
            var pollForChanges = string.Equals(environmentValue, "1", StringComparison.Ordinal) ||
                string.Equals(environmentValue, "true", StringComparison.OrdinalIgnoreCase);

            _usePollingFileWatcher = pollForChanges;
            _useActivePolling = pollForChanges;
        }

        /// <summary>
        /// Disposes the provider. Change tokens may not trigger after the provider is disposed.
        /// </summary>
        public void Dispose() => Dispose(true);

        /// <summary>
        /// Disposes the provider.
        /// </summary>
        /// <param name="disposing"><c>true</c> is invoked from <see cref="IDisposable.Dispose"/>.</param>
        protected virtual void Dispose(bool disposing)
        {
            _fileWatcher?.Dispose();
        }

        /// <summary>
        /// Destructor for <see cref="PhysicalFileProvider"/>.
        /// </summary>
        ~PhysicalFileProvider() => Dispose(false);

        /// <summary>
        /// The root directory for this instance.
        /// </summary>
        public string Root { get; }

        private string GetFullPath(string path)
        {
            if (PathUtils.PathNavigatesAboveRoot(path))
            {
                return null;
            }

            string fullPath;
            try
            {
                fullPath = Path.GetFullPath(Path.Combine(Root, path));
            }
            catch
            {
                return null;
            }

            if (!IsUnderneathRoot(fullPath))
            {
                return null;
            }

            return fullPath;
        }

        private bool IsUnderneathRoot(string fullPath)
        {
            return fullPath.StartsWith(Root, StringComparison.OrdinalIgnoreCase);
        }

        /// <summary>
        /// Locate a file at the given path by directly mapping path segments to physical directories.
        /// </summary>
        /// <param name="subpath">A path under the root directory</param>
        /// <returns>The file information. Caller must check <see cref="IFileInfo.Exists"/> property. </returns>
        public IFileInfo GetFileInfo(string subpath)
        {
            if (string.IsNullOrEmpty(subpath) || PathUtils.HasInvalidPathChars(subpath))
            {
                return new NotFoundFileInfo(subpath);
            }

            // Relative paths starting with leading slashes are okay
            subpath = subpath.TrimStart(_pathSeparators);

            // Absolute paths not permitted.
            if (Path.IsPathRooted(subpath))
            {
                return new NotFoundFileInfo(subpath);
            }

            var fullPath = GetFullPath(subpath);
            if (fullPath == null)
            {
                return new NotFoundFileInfo(subpath);
            }

            var fileInfo = new FileInfo(fullPath);
            if (FileSystemInfoHelper.IsExcluded(fileInfo, _filters))
            {
                return new NotFoundFileInfo(subpath);
            }

            return new PhysicalFileInfo(fileInfo);
        }

        /// <summary>
        /// Enumerate a directory at the given path, if any.
        /// </summary>
        /// <param name="subpath">A path under the root directory. Leading slashes are ignored.</param>
        /// <returns>
        /// Contents of the directory. Caller must check <see cref="IDirectoryContents.Exists"/> property. <see cref="NotFoundDirectoryContents" /> if
        /// <paramref name="subpath" /> is absolute, if the directory does not exist, or <paramref name="subpath" /> has invalid
        /// characters.
        /// </returns>
        public IDirectoryContents GetDirectoryContents(string subpath)
        {
            try
            {
                if (subpath == null || PathUtils.HasInvalidPathChars(subpath))
                {
                    return NotFoundDirectoryContents.Singleton;
                }

                // Relative paths starting with leading slashes are okay
                subpath = subpath.TrimStart(_pathSeparators);

                // Absolute paths not permitted.
                if (Path.IsPathRooted(subpath))
                {
                    return NotFoundDirectoryContents.Singleton;
                }

                var fullPath = GetFullPath(subpath);
                if (fullPath == null || !Directory.Exists(fullPath))
                {
                    return NotFoundDirectoryContents.Singleton;
                }

                return new PhysicalDirectoryContents(fullPath, _filters);
            }
            catch (DirectoryNotFoundException)
            {
            }
            catch (IOException)
            {
            }
            return NotFoundDirectoryContents.Singleton;
        }

        /// <summary>
        ///     <para>Creates a <see cref="IChangeToken" /> for the specified <paramref name="filter" />.</para>
        ///     <para>Globbing patterns are interpreted by <seealso cref="Microsoft.Extensions.FileSystemGlobbing.Matcher" />.</para>
        /// </summary>
        /// <param name="filter">
        /// Filter string used to determine what files or folders to monitor. Example: **/*.cs, *.*,
        /// subFolder/**/*.cshtml.
        /// </param>
        /// <returns>
        /// An <see cref="IChangeToken" /> that is notified when a file matching <paramref name="filter" /> is added,
        /// modified or deleted. Returns a <see cref="NullChangeToken" /> if <paramref name="filter" /> has invalid filter
        /// characters or if <paramref name="filter" /> is an absolute path or outside the root directory specified in the
        /// constructor <seealso cref="PhysicalFileProvider(string)" />.
        /// </returns>
        public IChangeToken Watch(string filter)
        {
            if (filter == null || PathUtils.HasInvalidFilterChars(filter))
            {
                return NullChangeToken.Singleton;
            }

            // Relative paths starting with leading slashes are okay
            filter = filter.TrimStart(_pathSeparators);

            return FileWatcher.CreateFileChangeToken(filter);
        }
    }

可以看到PhysicalFileProvider的构造行数,需要传递两个参数,root目录和ExclusionFilters类型的filters(过滤一些文件)

PhysicalFileProvider类的GetFileInfo会根据传递的文件名或子目录获取对应的FileInfo对象,并返回PhysicalFileInfo对象

FileProvider的简单使用:

 

 在项目下创建如下目录结构

class Program
    {
        static void Main(string[] args)
        {

            var fileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "files"));
            var fileInfo = fileProvider.GetFileInfo("data.txt");
            using (var streamReader = new StreamReader(fileInfo.CreateReadStream()))
            {
                var result = streamReader.ReadToEnd();
                Console.WriteLine(result);
            }
            var contents = fileProvider.GetDirectoryContents("sub");
            foreach (var item in contents)
            {
                Console.WriteLine(item.Name);
            }


            Console.Read();

        }
    }

 

二、 ManifestEmbeddedFileProvider

  

/// <summary>
    /// Represents a file embedded in an assembly.
    /// </summary>
    public class EmbeddedResourceFileInfo : IFileInfo
    {
        private readonly Assembly _assembly;
        private readonly string _resourcePath;

        private long? _length;

        /// <summary>
        /// Initializes a new instance of <see cref="EmbeddedFileProvider"/> for an assembly using <paramref name="resourcePath"/> as the base
        /// </summary>
        /// <param name="assembly">The assembly that contains the embedded resource</param>
        /// <param name="resourcePath">The path to the embedded resource</param>
        /// <param name="name">An arbitrary name for this instance</param>
        /// <param name="lastModified">The <see cref="DateTimeOffset" /> to use for <see cref="LastModified" /></param>
        public EmbeddedResourceFileInfo(
            Assembly assembly,
            string resourcePath,
            string name,
            DateTimeOffset lastModified)
        {
            _assembly = assembly;
            _resourcePath = resourcePath;
            Name = name;
            LastModified = lastModified;
        }

        /// <summary>
        /// Always true.
        /// </summary>
        public bool Exists => true;

        /// <summary>
        /// The length, in bytes, of the embedded resource
        /// </summary>
        public long Length
        {
            get
            {
                if (!_length.HasValue)
                {
                    using (var stream = _assembly.GetManifestResourceStream(_resourcePath))
                    {
                        _length = stream.Length;
                    }
                }
                return _length.Value;
            }
        }

        /// <summary>
        /// Always null.
        /// </summary>
        public string PhysicalPath => null;

        /// <summary>
        /// The name of embedded file
        /// </summary>
        public string Name { get; }

        /// <summary>
        /// The time, in UTC, when the <see cref="EmbeddedFileProvider"/> was created
        /// </summary>
        public DateTimeOffset LastModified { get; }

        /// <summary>
        /// Always false.
        /// </summary>
        public bool IsDirectory => false;

        /// <inheritdoc />
        public Stream CreateReadStream()
        {
            var stream = _assembly.GetManifestResourceStream(_resourcePath);
            if (!_length.HasValue)
            {
                _length = stream.Length;
            }

            return stream;
        }
    }

 

/// <summary>
    /// An embedded file provider that uses a manifest compiled in the assembly to
    /// reconstruct the original paths of the embedded files when they were embedded
    /// into the assembly.
    /// </summary>
    public class ManifestEmbeddedFileProvider : IFileProvider
    {
        private readonly DateTimeOffset _lastModified;

        /// <summary>
        /// Initializes a new instance of <see cref="ManifestEmbeddedFileProvider"/>.
        /// </summary>
        /// <param name="assembly">The assembly containing the embedded files.</param>
        public ManifestEmbeddedFileProvider(Assembly assembly)
            : this(assembly, ManifestParser.Parse(assembly), ResolveLastModified(assembly)) { }

        /// <summary>
        /// Initializes a new instance of <see cref="ManifestEmbeddedFileProvider"/>.
        /// </summary>
        /// <param name="assembly">The assembly containing the embedded files.</param>
        /// <param name="root">The relative path from the root of the manifest to use as root for the provider.</param>
        public ManifestEmbeddedFileProvider(Assembly assembly, string root)
            : this(assembly, root, ResolveLastModified(assembly))
        {
        }

        /// <summary>
        /// Initializes a new instance of <see cref="ManifestEmbeddedFileProvider"/>.
        /// </summary>
        /// <param name="assembly">The assembly containing the embedded files.</param>
        /// <param name="root">The relative path from the root of the manifest to use as root for the provider.</param>
        /// <param name="lastModified">The LastModified date to use on the <see cref="IFileInfo"/> instances
        /// returned by this <see cref="IFileProvider"/>.</param>
        public ManifestEmbeddedFileProvider(Assembly assembly, string root, DateTimeOffset lastModified)
            : this(assembly, ManifestParser.Parse(assembly).Scope(root), lastModified)
        {
        }

        /// <summary>
        /// Initializes a new instance of <see cref="ManifestEmbeddedFileProvider"/>.
        /// </summary>
        /// <param name="assembly">The assembly containing the embedded files.</param>
        /// <param name="root">The relative path from the root of the manifest to use as root for the provider.</param>
        /// <param name="manifestName">The name of the embedded resource containing the manifest.</param>
        /// <param name="lastModified">The LastModified date to use on the <see cref="IFileInfo"/> instances
        /// returned by this <see cref="IFileProvider"/>.</param>
        public ManifestEmbeddedFileProvider(Assembly assembly, string root, string manifestName, DateTimeOffset lastModified)
            : this(assembly, ManifestParser.Parse(assembly, manifestName).Scope(root), lastModified)
        {
        }

        internal ManifestEmbeddedFileProvider(Assembly assembly, EmbeddedFilesManifest manifest, DateTimeOffset lastModified)
        {
            if (assembly == null)
            {
                throw new ArgumentNullException(nameof(assembly));
            }

            if (manifest == null)
            {
                throw new ArgumentNullException(nameof(manifest));
            }

            Assembly = assembly;
            Manifest = manifest;
            _lastModified = lastModified;
        }

        /// <summary>
        /// Gets the <see cref="Assembly"/> for this provider.
        /// </summary>
        public Assembly Assembly { get; }

        internal EmbeddedFilesManifest Manifest { get; }

        /// <inheritdoc />
        public IDirectoryContents GetDirectoryContents(string subpath)
        {
            var entry = Manifest.ResolveEntry(subpath);
            if (entry == null || entry == ManifestEntry.UnknownPath)
            {
                return NotFoundDirectoryContents.Singleton;
            }

            if (!(entry is ManifestDirectory directory))
            {
                return NotFoundDirectoryContents.Singleton;
            }

            return new ManifestDirectoryContents(Assembly, directory, _lastModified);
        }

        /// <inheritdoc />
        public IFileInfo GetFileInfo(string subpath)
        {
            var entry = Manifest.ResolveEntry(subpath);
            switch (entry)
            {
                case null:
                    return new NotFoundFileInfo(subpath);
                case ManifestFile f:
                    return new ManifestFileInfo(Assembly, f, _lastModified);
                case ManifestDirectory d when d != ManifestEntry.UnknownPath:
                    return new NotFoundFileInfo(d.Name);
            }

            return new NotFoundFileInfo(subpath);
        }

        /// <inheritdoc />
        public IChangeToken Watch(string filter)
        {
            if (filter == null)
            {
                throw new ArgumentNullException(nameof(filter));
            }

            return NullChangeToken.Singleton;
        }

        private static DateTimeOffset ResolveLastModified(Assembly assembly)
        {
            var result = DateTimeOffset.UtcNow;

            if (!string.IsNullOrEmpty(assembly.Location))
            {
                try
                {
                    result = File.GetLastWriteTimeUtc(assembly.Location);
                }
                catch (PathTooLongException)
                {
                }
                catch (UnauthorizedAccessException)
                {
                }
            }

            return result;
        }
    }

可以读取嵌入资源的文件

在项目中添加user.txt,修改生成操作为嵌入资源

class Program
    {
        static void Main(string[] args)
        {
            var fileProvider = new EmbeddedFileProvider(Assembly.GetEntryAssembly());
            var fileInfo = fileProvider.GetFileInfo("user.txt");
            using (var streamReader = new StreamReader(fileInfo.CreateReadStream()))
            {
                Console.WriteLine(streamReader.ReadToEnd());
            }


            Console.Read();

        }
    }

 

三、CompositeFileProvider

  

/// <summary>
    /// Looks up files using a collection of <see cref="IFileProvider"/>.
    /// </summary>
    public class CompositeFileProvider : IFileProvider
    {
        private readonly IFileProvider[] _fileProviders;

        /// <summary>
        /// Initializes a new instance of the <see cref="CompositeFileProvider" /> class using a collection of file provider.
        /// </summary>
        /// <param name="fileProviders">The collection of <see cref="IFileProvider" /></param>
        public CompositeFileProvider(params IFileProvider[] fileProviders)
        {
            _fileProviders = fileProviders ?? new IFileProvider[0];
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="CompositeFileProvider" /> class using a collection of file provider.
        /// </summary>
        /// <param name="fileProviders">The collection of <see cref="IFileProvider" /></param>
        public CompositeFileProvider(IEnumerable<IFileProvider> fileProviders)
        {
            if (fileProviders == null)
            {
                throw new ArgumentNullException(nameof(fileProviders));
            }
            _fileProviders = fileProviders.ToArray();
        }

        /// <summary>
        /// Locates a file at the given path.
        /// </summary>
        /// <param name="subpath">The path that identifies the file. </param>
        /// <returns>The file information. Caller must check Exists property. This will be the first existing <see cref="IFileInfo"/> returned by the provided <see cref="IFileProvider"/> or a not found <see cref="IFileInfo"/> if no existing files is found.</returns>
        public IFileInfo GetFileInfo(string subpath)
        {
            foreach (var fileProvider in _fileProviders)
            {
                var fileInfo = fileProvider.GetFileInfo(subpath);
                if (fileInfo != null && fileInfo.Exists)
                {
                    return fileInfo;
                }
            }
            return new NotFoundFileInfo(subpath);
        }

        /// <summary>
        /// Enumerate a directory at the given path, if any.
        /// </summary>
        /// <param name="subpath">The path that identifies the directory</param>
        /// <returns>Contents of the directory. Caller must check Exists property.
        /// The content is a merge of the contents of the provided <see cref="IFileProvider"/>.
        /// When there is multiple <see cref="IFileInfo"/> with the same Name property, only the first one is included on the results.</returns>
        public IDirectoryContents GetDirectoryContents(string subpath)
        {
            var directoryContents = new CompositeDirectoryContents(_fileProviders, subpath);
            return directoryContents;
        }

        /// <summary>
        /// Creates a <see cref="IChangeToken"/> for the specified <paramref name="pattern"/>.
        /// </summary>
        /// <param name="pattern">Filter string used to determine what files or folders to monitor. Example: **/*.cs, *.*, subFolder/**/*.cshtml.</param>
        /// <returns>An <see cref="IChangeToken"/> that is notified when a file matching <paramref name="pattern"/> is added, modified or deleted.
        /// The change token will be notified when one of the change token returned by the provided <see cref="IFileProvider"/> will be notified.</returns>
        public IChangeToken Watch(string pattern)
        {
            // Watch all file providers
            var changeTokens = new List<IChangeToken>();
            foreach (var fileProvider in _fileProviders)
            {
                var changeToken = fileProvider.Watch(pattern);
                if (changeToken != null)
                {
                    changeTokens.Add(changeToken);
                }
            }

            // There is no change token with active change callbacks
            if (changeTokens.Count == 0)
            {
                return NullChangeToken.Singleton;
            }
            
            return new CompositeChangeToken(changeTokens);
        }

        /// <summary>
        /// Gets the list of configured <see cref="IFileProvider" /> instances.
        /// </summary>
        public IEnumerable<IFileProvider> FileProviders => _fileProviders;
    }
CompositeFileProvider 可以看成是一种组合模式,接收多个IFileProvider
获取IFileInfo的时候遍历
IFileProvider获取

class Program
    {
        static void Main(string[] args)
        {
            var services = new ServiceCollection();
            var provider1 = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "files"));

            var provider2 = new EmbeddedFileProvider(Assembly.GetEntryAssembly());

            provider1.Watch("data.txt").RegisterChangeCallback(state => { Console.WriteLine("Change"); }, "");
            services.AddTransient<IFileProvider>(p => provider1);
            services.AddTransient<IFileProvider>(p => provider2);
            services.AddTransient<CompositeFileProvider>();
            var provider = services.BuildServiceProvider();
            var fileProvider = provider.GetService<CompositeFileProvider>();

            var fileInfo = fileProvider.GetFileInfo("data.txt");
            using (var streamReader = new StreamReader(fileInfo.CreateReadStream()))
            {
                var result = streamReader.ReadToEnd();
                Console.WriteLine(result);
            }
            var contents = fileProvider.GetDirectoryContents("sub");
            foreach (var item in contents)
            {
                Console.WriteLine(item.Name);
            }


            Console.Read();

        }
    }

IFileProvider有Watch方法,可以监听某个文件,当文件被修改时,会触发相应的操作

 

总结:了解了netcore文件系统的原理,可以自定义一个自己的文件提供器。比如获取阿里云Oss的文件

posted @ 2020-03-21 16:39  蓝平凡  阅读(2003)  评论(1编辑  收藏  举报