前一篇用 Node.js 實作一個 Apache HTTP Server (二),完成了大部分 Apache 目錄頁面的功能,最後提到還有一些部分可以做優化:
- 取得檔案大小、創建時間、修改時間資訊,判斷檔案類型 icon
- 點擊目錄內的檔案名,可以直接造訪該檔案
- 大標題路徑與請求路徑一致
- 當造訪的目錄內有
index
為名的檔案時,不顯示目錄,而是該檔案 - 優化程式碼
目前進度
取得檔案大小、創建時間、修改時間資訊,判斷檔案類型 icon
fs.statSync() - 取得資源詳細資訊
Node.js 提供一個 API fs.statSync()
,它接受一個路徑參數,回傳該路徑檔案或資料夾的詳細資訊(包含了檔案大小、創建時間、修改時間的資訊)。前面已經透過 fs.readdir
取得資料夾列表的所有成員的名稱字串,接著只需要將請求路徑字串及成員的名稱字串連接再當作參數帶入 fs.statSync()
即可取得目錄頁面所有成員的詳細資訊
這邊任意將一個資源路徑帶入 fs.statSync()
,看看輸出的資料:
1 | Stats { |
其中我們需要的資訊為
- size 檔案大小(Byte)
- ctime 創建時間
- mtime 修改時間
另外需要注意 fs.statSync()
輸出的時間格式為 RFC3999Date 時間格式,我們需要處理一下將它轉為像是 2019-01-01 11:11
這樣的格式
透過函式處理
1 | var timeFormater = function(RFCdate) { |
目前每個資料夾目錄成員須具備的資訊有
- 檔名(或資料夾名)
- 檔案大小
- 創建時間
- 修改時間
將所有成員需要的資訊一一整理成物件
1 | if (fs.statSync(fullPath).isDirectory()) { |
1 | <tbody id="tbody"> |
目錄頁面成員類型icon (資料夾或檔案)
目前目錄頁面的成員無論是什麼檔案類型,一律都是顯示檔案的 icon,現在我們需要做判斷,資料夾就渲染資料夾 icon,其他的就渲染檔案 icon。
我們可以利用 art-template
的 if
功能配合正規式判斷,正規式的判斷方式很簡單,只要成員的名稱出現 .
我們可以直接判定它是具附檔名的一般檔案,檔名沒有出現 .
則一律當作資料夾處理。
1 | {{ each files }} |
點擊檔案名即可造訪該檔案
目前可以透過輸入網址造訪到單一檔案,但是點擊目錄頁面的檔案是沒有反應的,這裡可以透過 art-template
將請求路徑資料帶入 HTML a
標籤的 href
屬性即可。
將請求路徑與 port 號加上檔名字串接起來,傳入模板並帶入 href 屬性
1 | var htmlStr = template.render(templateFile.toString(), { |
修改模板
1 | <td><a class="icon file" href="http://127.0.0.1:{{ port + path + '/' + $value.fileName }}">{{ $value.fileName }}</a></td> |
大標題路徑與請求路徑一致
將請求路徑及帶入模板即可
1 | <h1 id="header">Index Of C:/nodejs/www{{ path }}</h1> |
當造訪的目錄內有 index 為名的檔案時,不顯示目錄,而是該檔案
dirFiles
為使用 fs.readdir()
所取出的資料夾成員陣列,透過迴圈及正規式判斷使否有成員名稱吻合 index.
1 | if (fs.statSync(fullPath).isDirectory()) { |
如果請求資料夾內有 index
為名的檔案,則直接回傳該檔案
1 | // 檢查該資料夾內是否有名為index的檔案,有則建立一個indexItem物件 |
優化程式碼
動態 Content-type
之前有一篇文章提到關於
Content-type
,若不清楚它是做什麼的可以先閱讀
伺服器傳送資源到客戶端時,檔案型態千百種,我們需要告訴客戶端瀏覽器當前的 response 檔案需要用什麼檔案類型來解析。
首先可以透過 Node.js 提供的 path
核心模組中的 path.extname
API 取得檔案副檔名
1 | // 引入核心模組 |
了解使用方式後,就可以製作一個函式,將常見的檔案類型和對應的 Content-type
寫起來
1 | var returnContentType = function (fileName) { |
使用
1 | // 如果有index,則回傳該檔案 |
將 error page 導向包裝成函式
1 | var toErrorPage = function () { |
接著將程式碼中所有的 return res.end('404 Not Found.')
替換成 toErrorPage()