Node 之fs模組 運用
一、實用小Demo
1.圖片上傳
// 1. 判斷伺服器上面有沒有 upload 目錄,沒有就建立這個目錄 // 2. 找出 html 目錄下面的所有的目錄,然後打印出來 const fs = require('fs'); fs.stat('upload', (err, stats) => { // 判斷有沒有 upload 目錄 if(err) { // 如果沒有 fs.mkdir('upload', (error) => { if(error) { console.log(error); return false; } else { console.log("建立 upload 目錄成功!"); } }) } else { // 如果有 console.log(stats.isDirectory()); console.log("有 upload 目錄,你可以做更多操作!"); } })
2.讀取目錄全部檔案
const fs = require('fs'); fs.readdir('node_modules', (err, files) => { if(err) { console.log(err); return false; } else { // 判斷是目錄還是資料夾 console.log(files); let filesArr = []; (function getFile(i) { // 迴圈結束 if(i == files.length) { // 打印出所有目錄 console.log("目錄:"); console.log(filesArr); return false; } // 判斷目錄是檔案還是資料夾 fs.stat('node_modules/' + files[i], (error, stats) => { if(stats.isDirectory()) { filesArr.push(files[i]); } // 遞迴呼叫 getFile(i+1); }) })(0) } })
二、實現遞迴建立目錄
我們建立一個函式,引數為一個路徑,按照路徑一級一級的建立資料夾目錄。
1、同步的實現
遞迴刪除檔案目錄 —— 同步const fs = require("fs"); const path = require("path"); // 同步建立檔案目錄 function mkPathSync(dirPath) { // path.sep 檔案路徑分隔符(mac 與 window 不同) // 轉變成陣列,如 ['a', 'b', 'c'] let parts = dirPath.split(path.sep); for(let i = 1; i <= parts.length; i++) { // 重新拼接成 a a/b a/b/c let current = parts.slice(0, i).join(path.sep); // accessSync 路徑不存在則丟擲錯誤在 catch 中建立資料夾 try { fs.accessSync(current); } catch(e) { fs.mkdirSync(current); } } } // 建立檔案目錄 mkPathSync(path.join("a", "b", "c"));
同步程式碼就是利用 accessSync
方法檢查檔案路徑是否存在,利用 try...catch...
進行錯誤捕獲,如果路徑不存在,則會報錯,會進入 catch
完成資料夾的建立。
2、非同步回撥的實現
遞迴刪除檔案目錄 —— 非同步回撥const fs = require("fs"); const path = require("path"); function mkPathAsync(dirPath, callback) { // 轉變成陣列,如 ['a', 'b', 'c'] let parts = dirPath.split(path.sep); let index = 1; // 建立資料夾方法 function next() { // 重新拼接成 a a/b a/b/c let current = parts.slice(0, index).join(path.sep); index++; // 如果路徑檢查成功說明已經有該檔案目錄,則繼續建立下一級 // 失敗則建立目錄,成功後遞迴 next 建立下一級 fs.access(current, err => { if (err) { fs.mkdir(current, next); } else { next(); } }); } next(); } // 建立檔案目錄 mkPathAsync(path.join("a", "b", "c"), () => { console.log("建立檔案目錄完成") }); // 建立檔案目錄完成
上面方法中沒有通過迴圈實現每次目錄的拼接,而是通過遞迴內部函式 next
的方式並維護 index
變數來實現的,在使用 access
的時候成功說明檔案目錄已經存在,就繼續遞迴建立下一級,如果存在 err
說明不存在,則建立資料夾。
3、非同步 async/await 的實現
上面兩種方式,同步阻塞程式碼,效能不好,非同步回撥函式巢狀效能好,但是維護性差,我們想要具備效能好,程式碼可讀性又好可以使用現在 NodeJS 中正流行的 async/await
的方式進行非同步程式設計,想了解 async/await
可以看 非同步發展流程 —— 非同步程式設計的終極大招 async/await 這篇文章。
使用 async
函式中 await
等待的非同步操作必須轉換成 Promise,以前我們都使用 util
模組下的 promisify
方法進行轉換,其實 promisify
方法的原理很簡單,我們在實現遞迴建立檔案目錄之前先實現 promisify
方法。
// 將一個非同步方法轉換成 Promise function promisify(fn) { return function (...args) { return new Promise((resolve, reject) => { fn.call(null, ...args, err => err ? reject() : resolve()); }); } }
其實 promisify
方法就是利用閉包來實現的,呼叫時傳入一個需要轉換成 Promise 的函式 fn
,返回一個閉包函式,在閉包函式中返回一個 Promise 例項,並同步執行了 fn
,通過 call
將閉包函式中的引數和回撥函式作為引數傳入了 fn
中,該回調在存在錯誤的時候呼叫了 Promise 例項的 reject
,否則呼叫 resolve
;
const fs = require("fs"); const path = require("path"); // 將 fs 中用到的方法轉換成 Promise const access = promisify(fs.access); const mkdir = promisify(fs.mkdir); // async/await 實現遞迴建立檔案目錄 async function mkPath(dirPath) { // 轉變成陣列,如 ['a', 'b', 'c'] let parts = dirPath.split(path.sep); for(let i = 1; i <= parts.length; i++) { // 重新拼接成 a a/b a/b/c let current = parts.slice(0, i).join(path.sep); // accessSync 路徑不存在則丟擲錯誤在 catch 中建立資料夾 try { await access(current); } catch(e) { await mkdir(current); } } } // 建立檔案目錄 mkPath(path.("a", "b", "c")).then(() => { console.log("建立檔案目錄完成"); }); // 建立檔案目錄完成
使用 async/await
的寫法,程式碼更像同步的實現方式,卻是非同步執行,所以同時兼顧了效能和程式碼的可讀性,優勢顯而易見,在使用 NodeJS 框架 Koa 2.x
版本時大量使用這種方式進行非同步程式設計。