1. 程式人生 > >SeaweedFS在.net core下的實踐方案

SeaweedFS在.net core下的實踐方案

 

一直對分散式的檔案儲存系統很感興趣,最開始關注淘寶的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 != 2)
                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"
}

我見了鬼了,誰有發現原因,請告訴我一下,拜託了