曾經做過一個學校的系統,主要用於老師上傳課件和佈置作業,學生學習和提交作業,當時也沒考慮那麼多,上傳的檔案全部都是放在同一個伺服器上了,沒想到幾年過去了,這個系統還一直在用著,長時間上傳的資原始檔使硬碟空間吃緊了,歷史資料還沒法存檔,由於這個契機吧,自己簡單實現了一個檔案分佈上傳的系統。下面稱為ufs。
一、系統架構圖
上圖描述了使用者上傳檔案和訪問檔案的流程走向,系統主要涉及ufs和下面的node。
ufs是web網站或者app上傳的統一介面,ufs根據配置把上傳的檔案分發到某個node上。
node會返回上傳結果給ufs,主要是上傳成功後的檔案url,ufs收到url再返回給上層應用。
上層應用獲取到url可以展示出來或者儲存到資料庫。
當使用者訪問資源的時候,直接從各個node獲取。
當某個node的可用儲存滿了,可以從ufs配置中刪除掉,然後加入新的node,刪除的node只要是服務不關閉,照樣可以訪問上面的檔案,具體配置看下面。
二、ufs配置
{
"ufs": {
"allowExts": [".txt", ".jpg", ".jpeg", ".png", ".bmp", ".doc", ".docx", ".xls", ".xlsx", ".ppt", ".pptx", ".pdf"],
"limitSize": 10485760,
"accessToken": "123456",
"downstreams": [
"http://node1.ufs.timeschip.com"
],
"test": {
"limitSize": 10485760,
"accessToken": "abcd"
}
}
}
ufs就是node的閘道器,提供了下面這幾個功能配置:
- allowExts陣列用於配置可以上傳的檔案型別
- limitSize限制檔案上傳的大小
- accessToken訪問ufs的令牌
- downstreams配置下游node節點
上面配置中,和ufs平級的還有一個test節點,裡面允許的子節點和ufs的子節點是一樣的。目的是為不同的應用提供上傳服務。此處的“test”就是應用名稱,這個名字需要和上層應用約定好,test應用上傳的時候表明是test,就會使用配置檔案中test的配置,如果配置檔案中沒有test這個節點配置,就會使用ufs節點下的預設配置。
這樣的配置方式就像該配置檔案本身(appsettings.json)的載入方式一樣,會使用指定的配置覆蓋預設配置,非常靈活。至於如何呼叫,我們後面再說。
三、ufs.node配置
{
"ufsNode": {
"mimeModifiers": [
{
"ext": ".htm3",
"type": "text/html",
"opt": "add"
},
{
"ext": ".mp4",
"opt": "remove"
}
],
"domain": "http://node1.ufs.loogn.com",
"physicalPath": "D:/WebSite_Core/ufsnode1/upload",
"virtualPath": "/ufs",
"cachePeriod": 604800,
"enableThumbnail": true,
"thumbnailExts": [
".jpg",
".png"
],
"allowIPs": [
"::1",
"127.0.0.1",
"122.114.222.186"
]
}
}
node的配置比ufs要多一些,畢竟真正的儲存和訪問功能都在node上:
- mimeModifiers陣列可以修改node服務的mime型別,每一項有三個子節點:ext為檔案字尾名,type為對映的ContentType, opt是列舉可取add、remove。
- domain為該node服務執行的域名,是上傳檔案url的一部分
- physicalPath為上傳的物理路徑,儲存的本地絕對路徑
- virtualPath為上傳的虛擬路徑,是上傳檔案url的一部分
- cachePeriod過期時間(秒)
- enableThumbnail是否啟用縮圖功能
- thumbnailExts表示哪些檔案型別可以使用縮圖功能,只能配置圖片格式
- allowIPs訪問白名單,即ufs服務的ip地址
當啟用縮率圖功能時,可以在url中加w和h引數來訪問想要的縮圖:
原圖:http://node1.ufs.loogn.com/app1/2019/05/10/abc.png
限寬200:http://node1.ufs.loogn.com/app1/2019/05/10/abc.png?w=200
限高200:http://node1.ufs.loogn.com/app1/2019/05/10/abc.png?h=200
限寬高100*200:http://node1.ufs.loogn.com/app1/2019/05/10/abc.png?w=100&h=200
所有的壓縮都是等比的,圖片不會變形,不管引數怎樣,圖片也不會放大。
從allowIPs配置可以看出,應用層訪問ufs是用過accessToken來驗證的,而ufs訪問node是通過在node中配置允許的ip地址來實現的。
四、應用層呼叫
由於公開的是http介面,所以任何支撐Http的語言都可以使用。
static async Task<string> UploadFile(string filePath)
{
HttpClient httpClient = new HttpClient();
httpClient.DefaultRequestHeaders.Add("accesstoken", "abcd");
httpClient.DefaultRequestHeaders.Add("app", "test");
var buffer = File.ReadAllBytes(filePath);
ByteArrayContent byteArray = new ByteArrayContent(buffer);
byteArray.Headers.Add("ext", Path.GetExtension(filePath));
var response = await httpClient.PostAsync("http://ufs.loogn.com/uploadfile", byteArray);
var result = await response.Content.ReadAsStringAsync();
return result;
}
http請求頭部需要傳輸三個引數:
- accesstoken為訪問ufs的令牌,對用ufs中的配置
- app為指定app的名稱,除了和ufs服務中選擇配置相關,node服務也會在physicalPath目錄下建立app同名目錄,用來存放這個應用上傳的檔案,如果沒有這個引數,node會放入default資料夾
- ext為上傳檔案的字尾名,注意,是帶.的(比如:.jpg而不是jpg)
檔案內容通過請求體POST到ufs公開上傳地址/uploadfile,響應的字串是json格式,如下:
{"success":true, "msg":"", "fileUrl":"http://node1.ufs.loogn.com/app1/2019/05/10/abc.png"}
如果上傳失敗,success為false,msg中有錯誤資訊。