使用环境:由于要存储用户上传的很多小文件,比如照片附件等。
插件Github地址:https://github.com/chrislusf/seaweedfs
下载好releases对应的安装包即可在本机进行搭建
搭建步骤:
1.通过cmd进入weed.exe对应目录
2.搭建主节点(master节点):
weed master
提示如上图即可在浏览器中输入
localhost:9333
出现此图即主节点搭建成功。
3.搭建数据节点,数据节点的搭建和主节点类似,只是参数不相同。
weed volume -dir="/seaweedfs/tmp" -max=5 -mserver="localhost:9333" -port=9090 -index=leveldb
volume代表是数据节点,dir是相对路径,mserver主节点地址,port为数据节点端口,-index的级别设置为leveldb将会设置为固化到硬盘,而不是文件都存储在内存中。
数据节点搭建成功后,localhost:9333的页面将会展示数据节点的端口等信息。
可以设置多个数据节点,端口不一致即可。
如上设置第二个数据节点的命令如下
volume -dir="/seaweedfs/tmp" -max=5 -mserver="localhost:9333" -port=9091 -index=leveldb
运行后截图:
至此,主节点和数据节点都搭建完毕。
为了方便实现,之前从github上面拷贝了一个项目,进行了简单的一下修改,可以完成文件的一般操作。
public interface ISeaweedFSService { FileHandleStatus SaveFileByStream(string fileName, Stream stream, out string errMsg); FileHandleStatus UpdateFileByStream(string fileId, string fileName, Stream stream, out string errMsg); BytesResponse GetFileBytes(string fileId); bool DeleteFile(string fileId,out string errMsg); } public class SeaweedFSService : ISeaweedFSService { IPkpmConfigService pkpmConfigService; //private HttpClient HttpClient { get; set; } //private RestClient client {get;set;} bool UsingPublicUrl; string masterUrl = string.Empty; public SeaweedFSService(IPkpmConfigService pkpmConfigService) { this.pkpmConfigService = pkpmConfigService; masterUrl = pkpmConfigService.SeaweedFSMasterUrl; UsingPublicUrl = pkpmConfigService.SeaweedFSUsingPublicUrl; var clientHandler = new HttpClientHandler { AllowAutoRedirect = true, }; //HttpClient = new HttpClient(clientHandler); //var client = new RestClient(masterUrl); } private AssignFileKeyResult GetFileId() { var url = masterUrl + RequestPathStrategy.AssignFileKey; var client = new RestClient(url); var request = new RestRequest(""); IRestResponse<AssignFileKeyResult> response = client.Execute<AssignFileKeyResult>(request); return response.Data; } /// <summary> /// 存储文件 /// </summary> /// <param name="fileName"></param> /// <param name="stream"></param> /// <returns></returns> public FileHandleStatus SaveFileByStream(string fileName, Stream stream, out string errMsg) { var TTL = string.Empty; var fileKeyResult = GetFileId(); var uploadUrl = UsingPublicUrl ? fileKeyResult.PublicUrl : fileKeyResult.Url; uploadUrl = "http://" + uploadUrl; // Upload file return new FileHandleStatus( fileKeyResult.FId, UploadFile(uploadUrl, fileKeyResult.FId, fileName, stream, TTL, out errMsg), uploadUrl ); } /// <summary> /// 更新文件 /// </summary> /// <param name="fileId"></param> /// <param name="fileName"></param> /// <param name="stream"></param> /// <returns></returns> public FileHandleStatus UpdateFileByStream(string fileId, string fileName, Stream stream, out string errMsg) { var targetUrl = GetTargetUrl(fileId); targetUrl = "http://" + targetUrl; var TTL = string.Empty; return new FileHandleStatus( fileId, UploadFile(targetUrl, fileId, fileName, stream, TTL, out errMsg), targetUrl ); } public BytesResponse GetFileBytes(string fileId) { var targetUrl = GetTargetUrl(fileId); targetUrl = "http://" + targetUrl; var request = new HttpRequestMessage( HttpMethod.Get, new Uri(targetUrl + "/" + fileId) ); var result = FetchBytesByRequest(request); return result; } private BytesResponse FetchBytesByRequest(HttpRequestMessage request) { BytesResponse result = new BytesResponse(); try { var client = new RestClient(request.RequestUri); var wrequest = new RestRequest(""); var bytes = client.DownloadData(wrequest); result.bytes = bytes; } finally { request.Dispose(); } return result; } private bool DeleteFile(string url, string fileId, out string errMsg) { var restRequest = new RestRequest(Method.DELETE); var client = new RestClient(url + "/" + fileId); var jsonResponse = client.Execute(restRequest); if (ConvertResponseStatusToException((int)jsonResponse.StatusCode, url, fileId, false, false, false, false, out errMsg)) { return true; } else { return false; } } public bool DeleteFile(string fileId, out string errMsg) { var targetUrl = GetTargetUrl(fileId); targetUrl = "http://" + targetUrl; var result = CheckFileExist(targetUrl, fileId); if (result) { result = DeleteFile(targetUrl, fileId, out errMsg); } else { errMsg = "文件不存在"; } return result; } private bool CheckFileExists(string fileId) { var targetUrl = GetTargetUrl(fileId); return CheckFileExist(targetUrl, fileId); } private HttpStatusCode FetchStatusCodeByRequest(HttpRequestMessage request) { HttpStatusCode statusCode = HttpStatusCode.Accepted; try { var restRequest = new RestRequest(Method.GET); var client = new RestClient(request.RequestUri); var response = client.Execute(restRequest); statusCode = response.StatusCode; //response = HttpClient.SendAsync(request).Result; //statusCode = response.StatusCode; } finally { request.Dispose(); } return statusCode; } private bool CheckFileExist(string url, string fileId) { var request = new HttpRequestMessage( HttpMethod.Head, new Uri(url + "/" + fileId) ); var statusCode = FetchStatusCodeByRequest(request); try { string errMsg = string.Empty; if (ConvertResponseStatusToException((int)statusCode, url, fileId, false, true, false, false, out errMsg)) { return true; } else { return false; } } catch (Exception) { return false; } } /// <summary> /// 通过fileId查询是否存在此文件,获得相关的URL /// </summary> /// <param name="fileId"></param> /// <returns></returns> private LookupVolumeResult FetchLookupVolumeResult(string fileId) { var url = masterUrl + RequestPathStrategy.LookupVolume + "?volumeId=" + fileId; var request = new HttpRequestMessage( HttpMethod.Get, new Uri(url) ); var restRequest = new RestRequest(Method.GET); var client = new RestClient(url); var response = client.Execute(restRequest); var content = response.Content; var jsonResponse = new JsonResponse(content, response.StatusCode); var obj = JsonConvert.DeserializeObject<LookupVolumeResult>(jsonResponse.Json, new JsonSerializerSettings() { MissingMemberHandling = MissingMemberHandling.Ignore }); return obj; } private string GetTargetUrl(string fileId) { var volumeResult = FetchLookupVolumeResult(fileId); var result = volumeResult.Locations[0]; return UsingPublicUrl ? result.PublicUrl : result.Url; } private bool ConvertResponseStatusToException(int statusCode, string url, string fileId, bool ignoreNotFound, bool ignoreRedirect, bool ignoreRequestError, bool ignoreServerError, out string ErrMsg) { ErrMsg = string.Empty; switch (statusCode / 100) { case 1: case 2: return true; case 3: if (ignoreRedirect) { return true; } ErrMsg = "Looks up a localized string similar to Fetch file from [{0}/{1}] has been redirected, response stats code is [{2}]".Fmt(url, fileId, statusCode); return false; case 4: if (statusCode == 404 && ignoreNotFound) { return false; } if (statusCode == 404) { ErrMsg = "Looks up a localized string similar to Fetch file from[{0}/{1}] has not been found, response stats code is [{2}]".Fmt(url, fileId, statusCode); return false; } if (ignoreRequestError) { return false; } ErrMsg = "Looks up a localized string similar to Fetch file from[{0}/{1}] has returned request error, response stats code is [{2}]".Fmt(url, fileId, statusCode); return false; case 5: if (ignoreServerError) { return false; } ErrMsg = "Looks up a localized string similar to Fetch file from[{0}/{1}] has returned request error, response stats code is [{2}]".Fmt(url, fileId, statusCode); return false; default: ErrMsg = "Looks up a localized string similar to Fetch file from[{0}/{1}] has returned request error, response stats code is [{2}]".Fmt(url, fileId, statusCode); return false; } } private long UploadFile(string url, string fileId, string filename, Stream inputStream, string ttl, out string outErrMsg) { outErrMsg = string.Empty; url = url + "/" + fileId; if (!string.IsNullOrEmpty(ttl)) url = url + "?ttl=" + ttl; //var request = new HttpRequestMessage( // HttpMethod.Post, // new Uri(url) //); var restRequest = new RestRequest(Method.POST); byte[] bytes = new byte[inputStream.Length]; inputStream.Read(bytes, 0, bytes.Length); // 设置当前流的位置为流的开始 inputStream.Seek(0, SeekOrigin.Begin); restRequest.AddFileBytes(filename, bytes, filename, "form-data"); var response = FetchJsonResultByRequest(restRequest, url); JsonResponse jsonResponse = new JsonResponse(response.Json, response.StatusCode); string errMsg = string.Empty; if (ConvertResponseStatusToException((int)jsonResponse.StatusCode, url, fileId, false, false, false, false, out errMsg)) { var obj = JsonConvert.DeserializeObject<JObject>(jsonResponse.Json); var jToken = obj.GetValue("size"); return jToken.Value<long>(); } else { outErrMsg = errMsg; return 0; } } private JsonResponse FetchJsonResultByRequest(RestRequest request, string url) { JsonResponse jsonResponse = null; try { var client = new RestClient(url); var response = client.Execute(request); //response = await HttpClient.SendAsync(request); var content = response.Content; jsonResponse = new JsonResponse(content, response.StatusCode); } catch (Exception) { } if (jsonResponse.Json.Contains(""error":"")) { var obj = JsonConvert.DeserializeObject<JObject>(jsonResponse.Json); var jToken = obj.GetValue("error"); if (jToken != null) { } } return jsonResponse; } }
基本上还是根据官网的文档来进行部署的,只是根据自己的使用情况进行了一点总结和修改。
记录一下方便后续使用