1. 程式人生 > >Node 之fs模組 運用

Node 之fs模組 運用

 fs.stat 檢測是檔案還是目錄     fs.mkdir 建立目錄      fs.writeFile 建立寫入檔案 *   fs.appendFile 追加檔案        fs.readFile 讀取檔案     fs.readdir 讀取目錄   fs.rename 重新命名          fs.rmdir 刪除目錄     fs.unlink 刪除檔案

一、實用小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 方法。

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

遞迴刪除檔案目錄 —— 非同步 async/await
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 版本時大量使用這種方式進行非同步程式設計。