一直对分布式的文件储存系统很感兴趣,最开始关注淘宝的TFS(Taobao File System),好像搁浅了,官方地址无法访问,github上面,各种编译问题,无意间发现了SeaweedFS

链接seaweedfs

测试了一番,写个应用的文章和.net core实践的短文分享一下

SeaweedFS如何使用

SeaweedFS的Releases下面下载成品,1.20(主要原因是懒,不想去编译)

运行命令

weed master

再挂载两个分布的服务

weed volume -dir="D:/FileService/Volume/1" -max=1000  -mserver="localhost:9333" -port=8080
weed volume -dir="D:/FileService/Volume/2" -max=1000  -mserver="localhost:9333" -port=8081

我们在访问一下

http://localhost:9333/dir/assign

返回可能是这样的内容

{"fid":"1,1642d6a0d7","url":"127.0.0.1:8081","publicUrl":"127.0.0.1:8081","count":1}

我们解释一下

fid是我们需要的上传的参数

publicUrl是我们实际上需要上传的地址

我们这次上传的目标地址是

http://publicUrl/fid
http://127.0.0.1:8081/1,1642d6a0d7

上传的参数file是对应的文件,上传类型是form-data,就是标准的html表单提交方式

返回你的类型可能是

{
"name": "150106109346115258.jpg",
"size": 206354,
"eTag": "9e663632"
}

这个etag,经常做web缓存的人,肯定不陌生,http缓存的策略

访问地址则为

http://127.0.0.1:8081/1,1642d6a0d7
http://127.0.0.1:8081/1/1642d6a0d7
http://127.0.0.1:8081/1/1642d6a0d7/150106109346115258.jpg

SeaweedFS支持多数据中心,这个在官方github有提到,SeaweedFS自带健康检查,内部走的GRPC做健康检查,所以请保持分布的服务端口,外界可访问,无论是docker还是虚拟机、VPS,最终上传还是走的那个端口

.Net Core下的实践

我们先把两个返回的实体对象做一下

    public class SeaweedFSDirAssignModel
{
public string Fid { get; set; }
public string Url { get; set; }
public string PublicUrl { get; set; }
public int Count { get; set; }
}
    public class SeaweedFSUploadResponse
{
public string Name { get; set; }
public int Size { get; set; }
public string ETag { get; set; }
}

我们再根据这两个实体,设计一个上传服务

    public interface IFileService
{
Task<SeaweedFSDirAssignModel> GetUploadFileUrlAsync(); Task<SeaweedFSUploadResponse> UploadFileAsync(string url,byte[] context);
}

再设计一个注入的参数

    public class SeaweedFSServiceConfiguration
{
public string BaseUrl { get; set; } = "localhost:9333";
public string DirAssign { get; set; } = "/dir/assign";
}

DirAssign这个是默认的参数,如果要用数据中心的话,这个就可以自定义修改了

    public class SeaweedFSService : IFileService
{
private SeaweedFSServiceConfiguration Configuration { get; } public SeaweedFSService(IOptions<SeaweedFSServiceConfiguration> options)
{
Configuration = options.Value;
} public async Task<SeaweedFSDirAssignModel> GetUploadFileUrlAsync()
{
using (var client = HttpClientFactory.Create())
{
var url = $"http://{Configuration.BaseUrl}{Configuration.DirAssign}";
var response = await client.GetAsync(url);
var body = await response.Content.ReadAsStringAsync();
var json = JsonConvert.DeserializeObject<SeaweedFSDirAssignModel>(body); return json;
}
} public async Task<SeaweedFSUploadResponse> UploadFileAsync(string url, byte[] context)
{
using (var client = HttpClientFactory.Create())
{
using (var multipartContext = new MultipartFormDataContent())
{
multipartContext.Add(
new ByteArrayContent(
context
),
"file"
); var fileUrl = $"{url}";
var response = await client.PostAsync(fileUrl, multipartContext);
var body = await response.Content.ReadAsStringAsync();
var json = JsonConvert.DeserializeObject<SeaweedFSUploadResponse>(body); return json;
}
}
}
}

在Startup.cs的注入一下

        public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); #region 注入服务
services.Configure<SeaweedFSServiceConfiguration>(options=> new SeaweedFSServiceConfiguration());
services.AddScoped<IFileService, SeaweedFSService>();
#endregion
}

测试文件上传

先写一个扩展方法,我们希望看见的返回地址是

http://127.0.0.1:8081/1,1642d6a0d7
http://127.0.0.1:8081/1/1642d6a0d7

这个地址的后者

实现如下

    internal static class SeaweedFSDirAssignModelExtension
{
public static string ToFileName(this SeaweedFSDirAssignModel response)
{
var splits = response.Fid.Split(",".ToCharArray(), StringSplitOptions.RemoveEmptyEntries); if (splits.Length != )
throw new ArgumentException($"String Split Fail, value:{response.Fid}"); return $"{splits[0]}/{splits[1]}";
}
}

写一个控制器测试上传

构建一下返回参数和入参

    public class UploadFileResponseModel
{
public string FileName { get; set; }
}
    public class UploadFileRequestModel
{
public IFormFile File { get; set; }
}

控制器代码如下

    [Route("api/[controller]")]
[ApiController]
public class FileController : ControllerBase
{
private IFileService FileService { get; } public FileController(IFileService fileService)
{
FileService = fileService;
} [HttpPost("Upload")]
public async Task<UploadFileResponseModel> Upload([FromForm]UploadFileRequestModel param)
{
#region IO读取
var stream = param.File.OpenReadStream();
var bytes = new byte[param.File.Length];
await stream.ReadAsync(bytes, 0, bytes.Length);
#endregion var fileInfo = await FileService.GetUploadFileUrlAsync();
var url = $"http://{fileInfo.PublicUrl}/{fileInfo.Fid}";
var uploadResponse = await FileService.UploadFileAsync(url, bytes); return new UploadFileResponseModel()
{
FileName = $"{fileInfo.ToFileName()}"
};
}
}

我们用postman测试一下

ok,上传成功,我们访问

http://localhost:9333/4,1ca657cf3f
http://localhost:9333/4/1ca657cf3f
http://127.0.0.1:8080/4,1ca657cf3f
http://127.0.0.1:8080/4/1ca657cf3f

前面两个地址会转跳到后面两个地址

后记

我这代码测试,会出现,不返回name字段的情况

{
"name": "150106109346115258.jpg",
"size": 206354,
"eTag": "9e663632"
}

这种json格式是直接上传的返回

但是我们这个上传服务会变成

{
"size": 206354,
"eTag": "9e663632"
}

我见了鬼了,谁有发现原因,请告诉我一下,拜托了

最新文章

  1. C++ malloc new 的区别
  2. [codeforces 391D2]Supercollider
  3. Jmeter正则表达式提取器的使用方法(转)
  4. JavaScript的角色巨变和Web技术的发展
  5. C# 模式窗口下更新进度条
  6. [转载]再谈iframe自适应高度
  7. c#回调函数写法
  8. [R] /usr/share/doc/apache2/README.Debian.gz
  9. [RxJS] Toggle A Stream On And Off With RxJS
  10. 你想不到的压缩方法:将javascript文件压缩成PNG图像存储
  11. Oracle EBS使用adpatch工具打patch过程【Z】
  12. android中使用setVideoURI()播放视频
  13. SharePoint 2010 用Event Receiver将文件夹自动变成approved状态 (1)
  14. Javac编译与JIT编译
  15. 利用Google浏览器调试js代码
  16. 基本MarkDown语法
  17. Button的五种点击事件
  18. Navicat for MySQL 安装和破解
  19. C++类模板和模板类
  20. ASP.NET Web API 跨域访问(CORS)要注意的地方

热门文章

  1. python入门008
  2. Jenkins+tomcat自动发布的热部署/重启及遇到的坑解决办法
  3. (转自MDN)CSS基础一定要看的包含块(containing block)
  4. JVM 学习笔记(三)
  5. java 数据结构(五):数据结构简述
  6. java 面向对象(二十一):属性的赋值顺序
  7. JavaScript:父页面与Iframe页面方法互调
  8. DirectX11 With Windows SDK--34 位移贴图
  9. Python 3基础教程8-if else语句
  10. 小谢第37问:关于websocket推送进度,本地保存进度条,然后跳出页面进入后再显示的问题