文件缓存支持缓存上级目录列表信息到index.csv,即使本地没有该文件,也能在列表中显示,首次下载后即可缓存访问该文件
This commit is contained in:
parent
ddefcca7b0
commit
0fa21dfbb0
|
@ -0,0 +1,95 @@
|
|||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.FileProviders.Physical;
|
||||
using NewLife;
|
||||
using NewLife.IO;
|
||||
|
||||
namespace Stardust.Extensions.Caches;
|
||||
|
||||
class CacheDirectoryContents : IDirectoryContents, IEnumerable<IFileInfo>, IEnumerable
|
||||
{
|
||||
private IEnumerable<IFileInfo> _entries;
|
||||
|
||||
private readonly String _directory;
|
||||
|
||||
private readonly ExclusionFilters _filters;
|
||||
|
||||
public Boolean Exists => Directory.Exists(_directory);
|
||||
|
||||
/// <summary>索引信息文件。列出扩展显示的文件内容</summary>
|
||||
public String IndexInfoFile { get; set; }
|
||||
|
||||
public CacheDirectoryContents(String directory)
|
||||
: this(directory, ExclusionFilters.Sensitive)
|
||||
{
|
||||
}
|
||||
|
||||
public CacheDirectoryContents(String directory, ExclusionFilters filters)
|
||||
{
|
||||
_directory = directory ?? throw new ArgumentNullException(nameof(directory));
|
||||
_filters = filters;
|
||||
}
|
||||
|
||||
public IEnumerator<IFileInfo> GetEnumerator()
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _entries.GetEnumerator();
|
||||
}
|
||||
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
EnsureInitialized();
|
||||
return _entries.GetEnumerator();
|
||||
}
|
||||
|
||||
private void EnsureInitialized()
|
||||
{
|
||||
try
|
||||
{
|
||||
_entries = (from info in new DirectoryInfo(_directory).EnumerateFileSystemInfos()
|
||||
where !CacheFileProvider.IsExcluded(info, _filters)
|
||||
select info).Select(info =>
|
||||
{
|
||||
if (info is FileInfo fileInfo)
|
||||
return new PhysicalFileInfo(fileInfo);
|
||||
return info is DirectoryInfo directoryInfo
|
||||
? (IFileInfo)new PhysicalDirectoryInfo(directoryInfo)
|
||||
: throw new InvalidOperationException("UnexpectedFileSystemInfo");
|
||||
});
|
||||
|
||||
if (!IndexInfoFile.IsNullOrEmpty())
|
||||
{
|
||||
var fi = _directory.CombinePath(IndexInfoFile).GetBasePath().AsFile();
|
||||
if (fi.Exists)
|
||||
{
|
||||
var csv = new CsvDb<FileInfoModel>((x, y) => x.Name.EqualIgnoreCase(y.Name))
|
||||
{
|
||||
FileName = fi.FullName
|
||||
};
|
||||
var fis = csv.FindAll();
|
||||
if (fis.Count > 0)
|
||||
{
|
||||
var list = _entries.ToList();
|
||||
foreach (var item in fis)
|
||||
{
|
||||
// 把fis里面的项添加到list
|
||||
if (!list.Any(e => e.Name.EqualIgnoreCase(item.Name)))
|
||||
list.Add(item);
|
||||
}
|
||||
|
||||
_entries = list;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex) when (ex is DirectoryNotFoundException or IOException)
|
||||
{
|
||||
_entries = Enumerable.Empty<IFileInfo>();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,17 +2,21 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Security.Policy;
|
||||
using Microsoft.AspNetCore.Mvc.Razor.Infrastructure;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
using Microsoft.Extensions.FileProviders.Internal;
|
||||
using Microsoft.Extensions.FileProviders.Physical;
|
||||
using Microsoft.Extensions.Primitives;
|
||||
using NewLife;
|
||||
using NewLife.Http;
|
||||
using NewLife.IO;
|
||||
using NewLife.Log;
|
||||
using NewLife.Web;
|
||||
|
||||
namespace Stardust.Extensions;
|
||||
namespace Stardust.Extensions.Caches;
|
||||
|
||||
/// <summary>文件缓存提供者。本地文件不存在时,从上级拉取</summary>
|
||||
public class CacheFileProvider : IFileProvider
|
||||
class CacheFileProvider : IFileProvider
|
||||
{
|
||||
#region 属性
|
||||
private static readonly Char[] _pathSeparators = new Char[2]
|
||||
|
@ -28,10 +32,21 @@ public class CacheFileProvider : IFileProvider
|
|||
|
||||
/// <summary>服务端地址。本地文件不存在时,将从这里下载</summary>
|
||||
public String Server { get; set; }
|
||||
|
||||
/// <summary>索引信息文件。列出扩展显示的文件内容</summary>
|
||||
public String IndexInfoFile { get; set; }
|
||||
#endregion
|
||||
|
||||
//public CacheFileProvider(String root) : this(root, ExclusionFilters.Sensitive) { }
|
||||
|
||||
/// <summary>
|
||||
/// 实例化
|
||||
/// </summary>
|
||||
/// <param name="root"></param>
|
||||
/// <param name="server"></param>
|
||||
/// <param name="filters"></param>
|
||||
/// <exception cref="ArgumentException"></exception>
|
||||
/// <exception cref="DirectoryNotFoundException"></exception>
|
||||
public CacheFileProvider(String root, String server, ExclusionFilters filters = ExclusionFilters.Sensitive)
|
||||
{
|
||||
if (!Path.IsPathRooted(root)) throw new ArgumentException("The path must be absolute.", nameof(root));
|
||||
|
@ -60,6 +75,11 @@ public class CacheFileProvider : IFileProvider
|
|||
return !fullPath.StartsWithIgnoreCase(Root) ? null : fullPath;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文件信息
|
||||
/// </summary>
|
||||
/// <param name="subpath"></param>
|
||||
/// <returns></returns>
|
||||
public IFileInfo GetFileInfo(String subpath)
|
||||
{
|
||||
if (String.IsNullOrEmpty(subpath) || HasInvalidPathChars(subpath)) return new NotFoundFileInfo(subpath);
|
||||
|
@ -71,7 +91,9 @@ public class CacheFileProvider : IFileProvider
|
|||
if (fullPath == null) return new NotFoundFileInfo(subpath);
|
||||
|
||||
// 本地不存在时,从服务器下载
|
||||
if (!File.Exists(fullPath) && Path.GetFileName(fullPath).Contains('.'))
|
||||
var fi = fullPath.AsFile();
|
||||
//if ((!fi.Exists || fi.LastWriteTime.AddDays(1) < DateTime.Now) && Path.GetFileName(fullPath).Contains('.'))
|
||||
if (!fi.Exists && Path.GetFileName(fullPath).Contains('.'))
|
||||
{
|
||||
var url = subpath.Replace("\\", "/");
|
||||
url = Server.Contains("{0}") ? Server.Replace("{0}", url) : Server + url.EnsureStart("/");
|
||||
|
@ -100,6 +122,12 @@ public class CacheFileProvider : IFileProvider
|
|||
return IsExcluded(fileInfo, _filters) ? new NotFoundFileInfo(subpath) : new PhysicalFileInfo(fileInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否存在
|
||||
/// </summary>
|
||||
/// <param name="fileInfo"></param>
|
||||
/// <param name="filters"></param>
|
||||
/// <returns></returns>
|
||||
public static Boolean IsExcluded(FileSystemInfo fileInfo, ExclusionFilters filters)
|
||||
{
|
||||
if (filters == ExclusionFilters.None) return false;
|
||||
|
@ -112,6 +140,11 @@ public class CacheFileProvider : IFileProvider
|
|||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取文件内容
|
||||
/// </summary>
|
||||
/// <param name="subpath"></param>
|
||||
/// <returns></returns>
|
||||
public IDirectoryContents GetDirectoryContents(String subpath)
|
||||
{
|
||||
try
|
||||
|
@ -122,9 +155,43 @@ public class CacheFileProvider : IFileProvider
|
|||
if (Path.IsPathRooted(subpath)) return NotFoundDirectoryContents.Singleton;
|
||||
|
||||
var fullPath = GetFullPath(subpath);
|
||||
|
||||
// 下载信息文件
|
||||
if (!IndexInfoFile.IsNullOrEmpty() && !Server.IsNullOrEmpty())
|
||||
{
|
||||
var fi = fullPath.CombinePath(IndexInfoFile).GetBasePath().AsFile();
|
||||
if (!fi.Exists || fi.LastWriteTime.AddDays(1) < DateTime.Now)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = subpath.Replace("\\", "/");
|
||||
url = Server.Contains("{0}") ? Server.Replace("{0}", url) : Server + url.EnsureStart("/");
|
||||
|
||||
using var client = new HttpClient();
|
||||
var html = client.GetString(url);
|
||||
|
||||
var links = Link.Parse(html, url);
|
||||
var list = links.Select(e => new FileInfoModel
|
||||
{
|
||||
Name = e.FullName,
|
||||
LastModified = e.Time.Year > 2000 ? e.Time : DateTime.Now,
|
||||
Exists = true,
|
||||
IsDirectory = false,
|
||||
}).ToList();
|
||||
|
||||
var csv = new CsvDb<FileInfoModel> { FileName = fi.FullName };
|
||||
csv.Write(list, false);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
XTrace.Log?.Debug("下载目录信息出错:{0}", ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fullPath == null || !Directory.Exists(fullPath)
|
||||
? NotFoundDirectoryContents.Singleton
|
||||
: new PhysicalDirectoryContents(fullPath, _filters);
|
||||
? NotFoundDirectoryContents.Singleton
|
||||
: new CacheDirectoryContents(fullPath, _filters) { IndexInfoFile = IndexInfoFile };
|
||||
}
|
||||
catch (DirectoryNotFoundException) { }
|
||||
catch (IOException) { }
|
||||
|
@ -132,6 +199,11 @@ public class CacheFileProvider : IFileProvider
|
|||
return NotFoundDirectoryContents.Singleton;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 监控文件改变
|
||||
/// </summary>
|
||||
/// <param name="filter"></param>
|
||||
/// <returns></returns>
|
||||
public IChangeToken Watch(String filter) => NullChangeToken.Singleton;
|
||||
|
||||
internal static Boolean HasInvalidPathChars(String path) => path.IndexOfAny(_invalidFileNameChars) != -1;
|
|
@ -4,7 +4,7 @@ using Microsoft.AspNetCore.Builder;
|
|||
using Microsoft.AspNetCore.Http;
|
||||
using NewLife.Log;
|
||||
|
||||
namespace Stardust.Extensions;
|
||||
namespace Stardust.Extensions.Caches;
|
||||
|
||||
/// <summary>
|
||||
/// 文件缓存扩展
|
||||
|
@ -32,7 +32,7 @@ public static class FileCacheExtensions
|
|||
app.UseDirectoryBrowser(new DirectoryBrowserOptions
|
||||
{
|
||||
RequestPath = new PathString(requestPath),
|
||||
FileProvider = new CacheFileProvider(sdk, uplinkServer),
|
||||
FileProvider = new CacheFileProvider(sdk, uplinkServer) { IndexInfoFile = "index.csv" },
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
using System.IO;
|
||||
using System;
|
||||
using Microsoft.Extensions.FileProviders;
|
||||
|
||||
namespace Stardust.Extensions.Caches;
|
||||
|
||||
class FileInfoModel : IFileInfo
|
||||
{
|
||||
public String Name { get; set; }
|
||||
|
||||
public Boolean Exists { get; set; }
|
||||
|
||||
public Boolean IsDirectory { get; set; }
|
||||
|
||||
public DateTimeOffset LastModified { get; set; }
|
||||
|
||||
public Int64 Length { get; set; }
|
||||
|
||||
public String PhysicalPath { get; set; }
|
||||
|
||||
public Stream CreateReadStream() => throw new NotImplementedException();
|
||||
}
|
|
@ -10,6 +10,7 @@ using NewLife.Log;
|
|||
using NewLife.Serialization;
|
||||
using Stardust.Data.Nodes;
|
||||
using Stardust.Extensions;
|
||||
using Stardust.Extensions.Caches;
|
||||
using Stardust.Monitors;
|
||||
using Stardust.Server.Services;
|
||||
using XCode;
|
||||
|
|
|
@ -7,6 +7,7 @@ using NewLife.Cube.Extensions;
|
|||
using NewLife.Log;
|
||||
using Stardust.Data.Configs;
|
||||
using Stardust.Extensions;
|
||||
using Stardust.Extensions.Caches;
|
||||
using Stardust.Server.Services;
|
||||
using Stardust.Web.Services;
|
||||
using XCode;
|
||||
|
|
Loading…
Reference in New Issue