SeaweedFS在.net core下的实践方案(续一)
前言
我们之前已经完成了SeaweedFS在.net core下的使用了,但是说实话,还是不够,于是,我的目光盯住了IApplicationBuilder的扩展方法UseStaticFiles
这个可是好东西啊,我们访问资源的静态文件映射,你懂我的意思吧,对这里下手~
前戏
开工之前,我们转到定义看看
StaticFileOptions,这个就是我们自定义乱嗨的前提
它有两个,我们DIY需要用到的参数
RequestPath、FileProvider
顾名思义,前者是访问路径的前置地址
RequestPath的值是wwwroot,那么我们访问
http://url/wwwroot/XX.后缀 才会触发这个,而且,一定要是带后缀的才触发
后者是,前置地址触发的基础上才调用的
我们给他安排一下
实现
因为核心参数FileProvider类型为IFileProvider,所以,我们写一个实现类吧
public class SeaweedFSFileProvider : 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();
}
}
我们现在需要用到的是GetFileInfo这个方法,另外两个,并不会触发(严谨一点说,GetDirectoryContents,我们访问无论是资源目录路径,还是完整的资源路径,都不会触发)
比如我们wwwroot这个文件夹是资源路径
无论是
http://url/wwwroot/ 还是 http://url/wwwroot,都不触发
这个也没触发~
emmmmm,可能研究太浅了,这两个接口方法是给其他实现类提供的定制化功能?比如FileServer?
GetFileInfo方法的返回值是IFileInfo
这个接口,触发文件返回的顺序是
Exists属性->Length属性->LastModified属性->Exists属性->PhysicalPath属性->CreateReadStream方法
我们写一个实现
public class SeaweedFSFileInfo : IFileInfo
{
public bool Exists { get; set; } public long Length => new MemoryStream(Context).Length; public string PhysicalPath { get; set; } public string Name { get; set; } public DateTimeOffset LastModified { get; } public bool IsDirectory => false; private byte[] Context { get; } public SeaweedFSFileInfo()
{
} public SeaweedFSFileInfo(string physicalPath, byte[] context)
{
Context = context;
PhysicalPath = physicalPath;
Name = Path.GetFileName(PhysicalPath);
LastModified = DateTimeOffset.Now;
Exists = true;
} public Stream CreateReadStream()
{
return new MemoryStream(Context);
}
}
我们修改一下SeaweedFSFileProvider,这里注入一个IFileService
因为我们希望整个SeaweedFSFileProvider他只依赖于IFileService,而不过多依赖SeaweedFS的实现,不会让代码简洁性受损
public class SeaweedFSFileProvider : IFileProvider
{
private IFileService Service { get; } public SeaweedFSFileProvider(IFileService service)
{
Service = service;
} public IDirectoryContents GetDirectoryContents(string subpath)
{
throw new NotImplementedException();
} public IFileInfo GetFileInfo(string subpath)
{
throw new NotImplementedException();
} public IChangeToken Watch(string filter)
{
throw new NotImplementedException();
}
}
我们转到接口IFileService,写一个接口
public interface IFileService
{
Task<SeaweedFSDirAssignModel> GetUploadFileUrlAsync(); Task<SeaweedFSUploadResponse> UploadFileAsync(string url,byte[] context); IFileInfo GetFileInfo(string subpath);
}
再转到实现类
增加这个实现
public IFileInfo GetFileInfo(string subpath)
{
throw new NotImplementedException();
}
我们去外层的SeaweedFSFileProvider修改一下
public class SeaweedFSFileProvider : IFileProvider
{
private IFileService Service { get; } public SeaweedFSFileProvider(IFileService service)
{
Service = service;
} public IDirectoryContents GetDirectoryContents(string subpath)
{
throw new NotImplementedException();
} public IFileInfo GetFileInfo(string subpath)
{
return Service.GetFileInfo(subpath);
} public IChangeToken Watch(string filter)
{
throw new NotImplementedException();
}
}
这样IFileService 里面变成什么样,都跟这层没关系了
我们定义一个IFileInfoFactory
public interface IFileInfoFactory
{
bool Contains(string filepath); IFileInfo GetFileInfo(string filepath); IFileInfo AddFileInfo(string filepath, byte[] context); IFileInfo AddNotExists(string filepath);
}
再写一个默认实现
public class FileInfoFactory: IFileInfoFactory
{
private List<IFileInfo> FileInfo { get; } = new List<IFileInfo>(); public bool Contains(string filepath)
{
return FileInfo.Any(file => file.PhysicalPath.Equals(filepath));
} public IFileInfo GetFileInfo(string filepath)
{
return FileInfo.FirstOrDefault(file => file.PhysicalPath.Equals(filepath));
} public IFileInfo AddFileInfo(string filepath,byte[] context)
{
var info = new SeaweedFSFileInfo(filepath, context);
FileInfo.Add(info); return info;
} public IFileInfo AddNotExists(string filepath)
{
var info = new SeaweedFSFileInfo();
FileInfo.Add(info); return info;
}
}
我们修改一下
SeaweedFSService的默认实现,增加一个注入IFileInfoFactory
private IFileInfoFactory FileInfoFactory { get; } public SeaweedFSService(IOptions<SeaweedFSServiceConfiguration> options, IFileInfoFactory fileInfoFactory)
{
Configuration = options.Value;
FileInfoFactory = fileInfoFactory;
}
我们实现一下
GetFileInfo方法
public IFileInfo GetFileInfo(string subpath)
{
using (var client = HttpClientFactory.Create())
{
var path = subpath.Replace(Path.GetExtension(subpath), "");
var splits = path.Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); if (splits.Length == )
{
return FileInfoFactory.AddNotExists(subpath);
}
else
{
var fid = $"{splits[0]},{splits[1]}"; var response = client.GetAsync($"http://{Configuration.BaseUrl}/{fid}")
.GetAwaiter()
.GetResult(); if (response.StatusCode == HttpStatusCode.NotFound)
{
return FileInfoFactory.AddNotExists(subpath);
}
else
{
var context = response.Content;
var bytes = context.ReadAsByteArrayAsync()
.GetAwaiter()
.GetResult(); if (FileInfoFactory.Contains(subpath))
{
return FileInfoFactory.GetFileInfo(subpath);
}
else
{
return FileInfoFactory.AddFileInfo(subpath, bytes);
}
}
}
}
}
这个时候,我们测试一下
大功告成,撒花
优化
但是我们可能场景是这个文件上传了,就不再修改了,修改后文件,变成新路径,这样,文件就始终是静态的,那么这样反复http请求就没意义了
所以,我们修改一下
private SeaweedFSServiceConfiguration Configuration { get; }
private IFileInfoFactory FileInfoFactory { get; }
private IDistributedCache Cache { get; } public SeaweedFSService(IOptions<SeaweedFSServiceConfiguration> options, IFileInfoFactory fileInfoFactory, IDistributedCache cache)
{
Configuration = options.Value;
FileInfoFactory = fileInfoFactory;
Cache = cache;
}
增加了一个分布式缓存
我们就找这个缓存,能不能找到,还能找到,就说明已经缓存了这个文件信息,就不再走http
修改一下GetFileInfo
public IFileInfo GetFileInfo(string subpath)
{
var key = $"Distributed_Files_{subpath}"; var contextBytes = Cache.Get(key); if (contextBytes != null && FileInfoFactory.Contains(subpath))
{
return FileInfoFactory.GetFileInfo(subpath);
}
else
{
using (var client = HttpClientFactory.Create())
{
var path = subpath.Replace(Path.GetExtension(subpath), "");
var splits = path.Split("/".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); Cache.Set(key, new byte[] { }); if (splits.Length == )
{
return FileInfoFactory.AddNotExists(subpath);
}
else
{
var fid = $"{splits[0]},{splits[1]}"; var response = client.GetAsync($"http://{Configuration.BaseUrl}/{fid}")
.GetAwaiter()
.GetResult(); if (response.StatusCode == HttpStatusCode.NotFound)
{
return FileInfoFactory.AddNotExists(subpath);
}
else
{
var context = response.Content;
var bytes = context.ReadAsByteArrayAsync()
.GetAwaiter()
.GetResult(); if (FileInfoFactory.Contains(subpath))
{
return FileInfoFactory.GetFileInfo(subpath);
}
else
{
return FileInfoFactory.AddFileInfo(subpath, bytes);
}
}
}
}
}
}
这样访问的地址,缓存没失效之前,并且在文件缓存里面,就不再走http请求了
附
我们附上入口的代码
ConfigureServices方法内增加
services.AddDistributedMemoryCache();
这样就启用了默认的分布式缓存接口,后期要替换的实现,只用更换这里的具体实现就好了,我们不依赖具体实现
Configure方法内增加代码
using (var services = app.ApplicationServices.CreateScope())
{
var fileService = services.ServiceProvider.GetRequiredService<IFileService>(); app.UseStaticFiles(
new StaticFileOptions
{
RequestPath = "/Resource",
FileProvider = new SeaweedFSFileProvider(fileService)
}
);
}
SeaweedFS在.net core下的实践方案(续一)的更多相关文章
- SeaweedFS在.net core下的实践方案
一直对分布式的文件储存系统很感兴趣,最开始关注淘宝的TFS(Taobao File System),好像搁浅了,官方地址无法访问,github上面,各种编译问题,无意间发现了SeaweedFS 链接s ...
- 【转】.NET(C#):浅谈程序集清单资源和RESX资源 关于单元测试的思考--Asp.Net Core单元测试最佳实践 封装自己的dapper lambda扩展-设计篇 编写自己的dapper lambda扩展-使用篇 正确理解CAP定理 Quartz.NET的使用(附源码) 整理自己的.net工具库 GC的前世与今生 Visual Studio Package 插件开发之自动生
[转].NET(C#):浅谈程序集清单资源和RESX资源 目录 程序集清单资源 RESX资源文件 使用ResourceReader和ResourceSet解析二进制资源文件 使用ResourceM ...
- .net core下简单构建高可用服务集群
一说到集群服务相信对普通开发者来说肯定想到很复杂的事情,如zeekeeper ,反向代理服务网关等一系列的搭建和配置等等:总得来说需要有一定经验和规划的团队才能应用起来.在这文章里你能看到在.net ...
- .Net core下的配置设置(二)——Option
我在前面的文章.Net core下的配置设置(一)——Configuration中介绍了.net core下配置文件的读取方法,在.net core中,直接从Configuration对象中读取的并不 ...
- 基于OVS的VLAN虚拟化简易实践方案
基于OVS的VLAN虚拟化简易实践方案 前言 本实验基于ovs的vlan流表匹配,根据端口进行vlan标签插入.手工配置ovs,使其具有vlan虚拟化方案. 实验拓扑 ---- ---- | h1 | ...
- .net core 下使用StackExchange的Redis库访问超时解决
原文:.net core 下使用StackExchange的Redis库访问超时解决 目录 问题:并发稍微多的情况下Redis偶尔返回超时 给出了参考网址? 结论 小备注 引用链接 问题:并发稍微多的 ...
- .NET CORE下最快比较两个文件内容是否相同的方法 - 续
.NET CORE下最快比较两个文件内容是否相同的方法 - 续 在上一篇博文中, 我使用了几种方法试图找到哪个是.NET CORE下最快比较两个文件的方法.文章发布后,引起了很多博友的讨论, 在此我对 ...
- .NET Core 下的 API 网关
网关介绍 网关其实就是将我们写好的API全部放在一个统一的地址暴露在公网,提供访问的一个入口.在 .NET Core下可以使用Ocelot来帮助我们很方便的接入API 网关.与之类似的库还有Proxy ...
- nginx及其常用实践方案
nginx及其常用实践方案 1.概述 1.1 什么是nginx? 1.2 什么是反向代理? 2.nginx常用命令 3.ningx配置实践 3.1 nginx.conf基础配置项 3.2 http 反 ...
随机推荐
- .net面试技术要点总结
[整理]待毕业.Net码农就业求职储备 本文题目来源于互联网,仅供即将从学校毕业的.Net码农(当然,我本人也是菜逼一个)学习之用.当然,学习了这些题目不一定会拿到offer,但是针对就业求职做些 ...
- [Mybatis]Mybatis常用操作
Mybatis是目前国内比较流行的ORM框架,特点是可以写灵活的SQL语句,非常适合中小企业的面向数据库开发. 本文总结自己开发过程中常用的Mybatis操作. 一.插入操作 主键自增插入单条 < ...
- springbean 生命周期
springbean 和java对象得区别: 1.对象:任何符合java语法规则实例化出来的对象 2.springbean: 是spring对普通对象进行了封装为BeanDefinition,bean ...
- 赋值,逻辑,运算符, 控制流程之if 判断
赋值运算 (1). 增量运算 age += 1 # age = age + 1 print(age) age -= 10 # age = age - 10 (2).交叉赋值 x = 111 y = 2 ...
- hihoCoder 1041 国庆出游 最详细的解题报告
题目来源:国庆出游 解题思路(下面是大神的写的): 把题目中的序列称作S,树称作T.那么对于S中的任意节点x,x的子孙节点如果在S出现的话,那么这个子孙节点的位置是有一定要求的:x的所有子孙节点在S中 ...
- Illegal instant due to time zone offset transition (Asia/Shanghai)_夏令时问题
项目报错信息: Connot parse "1991-04-14",illegal instant due to time zone offset transition(Asia/ ...
- webStrom中配置nodejs
1.安装nodejs 下载地址:node.js:https://nodejs.org/download/ 按照提示安装即可 2.安装WebStrom 按照提示安装即可 下载地址:webstorm:ht ...
- Burp Suite Target Module - 目标模块
模块目的之一:获取网站分析 1.从Proxy - HTTP history界面选中需要加入Target Scope的Host 地址,右击,选中Add to Scope. 2.打开Target - Sc ...
- Lua骚操作——三元条件运算符
本文地址:https://www.cnblogs.com/oberon-zjt0806/p/13337577.html 本文参考了这篇文章 三元运算符 (如果您已经了解什么是三元运算符,请大胆第前往下 ...
- SmartMS如何使用二次验证码/虚拟MFA/两步验证/谷歌身份验证器?
一般点账户名——设置——安全设置中开通虚拟MFA两步验证 具体步骤见链接 SmartMS如何使用二次验证码/虚拟MFA/两步验证/谷歌身份验证器? 二次验证码小程序于谷歌身份验证器APP的优势 1.无 ...