node 的fs
文檔密密麻麻的 api 很是多,畢竟全面支持對文件系統的操做。文檔組織的很好,操做基本分爲文件操做、目錄操做、文件信息、流這個大方面,編程方式也支持同步、異步和 Promise。html
本文記錄了幾個文檔中沒詳細描寫的問題,能夠更好地串聯fs
文檔思路:前端
文件描述符是一個非負整數。它是一個索引值,操做系統能夠根據它來找到對應的文件。node
在 fs 的不少底層 api 中,須要用到文件描述符。在文檔中,描述符一般用fd
來表明。例如:fs.read(fd, buffer, offset, length, position, callback)
。與這個 api 相對應的是:fs.readFile(path[, options], callback)
。web
由於操做系統對文件描述符的數量有限制,所以在結束文件操做後,別忘記 close:算法
const fs = require("fs");
fs.open("./db.json", "r", (err, fd) => {
if (err) throw err;
// 文件操做...
// 完成操做後,關閉文件
fs.close(fd, err => {
if (err) throw err;
});
});
全部文件系統的 api 都有同步和異步兩種形式。編程
不推薦使用同步 api,會阻塞線程。json
try {
const buf = fs.readFileSync("./package.json");
console.log(buf.toString("utf8"));
} catch (error) {
console.log(error.message);
}
異步寫法寫起來容易進入回調地獄。api
fs.readFile("./package.json", (err, data) => {
if (err) throw err;
console.log(data.toString("utf8"));
});
在 node v12 以前,須要本身藉助 promise 封裝:promise
function readFilePromise(path, encoding = "utf8") {
const promise = new Promise((resolve, reject) => {
fs.readFile(path, (err, data) => {
if (err) return reject(err);
return resolve(data.toString(encoding));
});
});
return promise;
}
readFilePromise("./package.json").then(res => console.log(res));
在 node v12 中,引入了 fs Promise api。它們返回 Promise 對象而不是使用回調。 API 可經過 require('fs').promises
訪問。如此一來,開發成本更低了。frontend
const fsPromises = require("fs").promises;
fsPromises
.readFile("./package.json", {
encoding: "utf8",
flag: "r"
})
.then(console.log)
.catch(console.error);
fs.Dir 類:封裝了和文件目錄相關的操做
fs.Dirent 類:封裝了目錄項的相關操做。例如判斷設備類型(字符、塊、FIFO 等)。
它們之間的關係,經過代碼展現:
const fsPromises = require("fs").promises;
async function main() {
const dir = await fsPromises.opendir(".");
let dirent = null;
while ((dirent = await dir.read()) !== null) {
console.log(dirent.name);
}
}
main();
fs.Stats 類:封裝了文件信息相關的操做。它在fs.stat()
的回調函數中返回。
fs.stat("./package.json", (err, stats) => {
if (err) throw err;
console.log(stats);
});
注意,關於檢查文件是否存在:
在 nodejs 中,stream 是個很是重要的庫。不少庫的 api 都是基於 stream 來封裝的。例以下面要說的 fs 中的 ReadStream 和 WriteStream。
fs 自己提供了 readFile 和 writeFile,它們好用的代價就是性能有問題,會將內容一次所有載入內存。可是對於幾 GB 的大文件,顯然會有問題。
那麼針對大文件的解決方案天然是:一點點讀出來。這就須要用到 stream 了。以 readStream 爲例,代碼以下:
const rs = fs.createReadStream("./package.json");
let content = "";
rs.on("open", () => {
console.log("start to read");
});
rs.on("data", chunk => {
content += chunk.toString("utf8");
});
rs.on("close", () => {
console.log("finish read, content is:\n", content);
});
藉助 stream 的 pipe,一行快速封裝一個大文件的拷貝函數:
function copyBigFile(src, target) {
fs.createReadStream(src).pipe(fs.createWriteStream(target));
}