fs 模塊
Node.js 提供一組類似 UNIX(POSIX)標准的文件操作 API。 Node 導入文件系統模塊(fs)。Node.js 文件系統(fs 模塊)模塊中的方法均有異步和同步版本,例如讀取文件內容的函數有異步的 fs.readFile() 和同步的 fs.readFileSync()。異步的方法函數最后一個參數為回調函數,回調函數的第一個參數包含了錯誤信息(error)。最好使用異步方法,比起同步,異步方法性能更高,速度更快,而且沒有阻塞(重點)。對於流量較大的服務器,最好還是采用異步操作,同步操作時,只有前一個操作結束,才會開始后一個操作,如果某個操作特別耗時(常常發生在讀寫數據時),會導致整個程序停頓
常用方法
操作 | 異步方法 | 同步方法 |
---|---|---|
打開文件 | fs.open(path, flags[, mode], callback) | fs.openSync(path, flags[, mode]) |
文件信息 | fs.stat(path[, options], callback) | fs.statSync(path[, options]) |
新建文件 | fs.appendFile(path, data[, options], callback) | fs.appendFileSync(path, data[, options]) |
寫入文件 | fs.writeFile(file, data[, options], callback) | fs.writeFileSync(file, data[, options]) |
讀取文件 | fs.read() | |
讀取文件 | fs.readFile(path[, options], callback) | fs.readFileSync(path[, options]) |
重命名文件 | fs.rename(oldPath, newPath, callback) | fs.renameSync(oldPath, newPath) |
關閉文件 | fs.close(fd, callback) | fs.closeSync(fd) |
截取文件 | fs.ftruncate(fd[, len], callback) | fs.ftruncateSync(fd[, len]) |
刪除文件 | fs.unlink(path, callback) | fs.unlinkSync(path) |
文件存在 | fs.stat() / fs.access() | fs.existsSync(path) |
監聽文件 | fs.watchFile(filename[, options], listener) | |
停止監聽 | fs.unwatchFile(filename[, listener]) | |
打開大文件 | fs.createReadStream(path[, options]) | |
寫入大文件 | fs.createWriteStream(path[, options]) | |
創建目錄 | fs.mkdir(path[, options], callback) | fs.mkdirSync(path[, options]) |
讀取目錄 | fs.readdir(path[, options], callback) | fs.readdirSync(path[, options]) |
刪除目錄 | fs.rmdir(path, callback) | fs.rmdirSync(path) |
form 信息創建文件
form 表單進行一個 post 提交,在瀏覽器打開 127.0.0.1:9527,此時輸入表單信息,填寫用戶名/密碼/備注等信息.點擊提交之后會直接在當前目錄下創建一個 user.txt 的文件,使用 writeFileSync()同步方法進行創建
writeFileSync()方法
function router(p) {
......
"/": (request, response) => {
response.writeHead(200, { "Content-type": "text/html;charset=utf-8" });
createForm(response);
response.end();
},
"/login": (request, response) => {
let totalData = "";
request.on("data", data => {
totalData += data;
});
request.on("end", () => {
response.writeHead(200, { "Content-type": "text/html;charset=utf-8" });
//username=liudehua&password=123456&remark=%E6%88%91%E6%98%AF%E5%88%98%E5%BE%B7%E5%8D%8E%2C%E6%88%91%E6%98%AF%E4%B8%80%E5%90%8D%E6%AD%8C%E6%89%8B
//username=liudehua&password=123456&remark=我是劉德華,我是一名歌手
let decodeData = decodeURIComponent(totalData); //解決中文亂碼
fs.writeFileSync(path.join(__dirname, "/user.txt"), decodeData);
response.end();
});
},
......
}
function createForm(response) {
response.write("<form method='post' action='login'>");
response.write("<div>用戶名:</div><input type='text' name='username'>");
response.write("</br>");
response.write("<div>密碼:</div><input type='text' name='password'>");
response.write("</br>");
response.write(
"<div>備注:</div><textarea rows='10' cols='30' name='remark'></textarea>"
);
response.write("</br>");
response.write("<input type='submit' value='提交' />");
response.write("</br>");
}
但是在 node 開發中,同步編程在使用上並沒有什么優勢,尤其文件讀寫操作使用異步更好一些
writeFile()的回調函數的形式進行文件寫入
let decodeData = decodeURIComponent(totalData); //解決中文亂碼
fs.writeFile(path.join(__dirname, "/user.txt"), decodeData, err => {
if (err) throw err;
response.end();
});
response.end();
javascript 在 es6 的之后的異步編程的形式發生了一些改變,promise 的引入讓異步編程顯得更加優雅
//創建file.js
let fs = require("fs");
module.exports = {
write: function(filename, data, options) {
return new Promise((resolve, reject) => {
fs.writeFile(filename, data, options, err =>
err === null ? resolve(filename) : reject(err)
);
});
}
};
//app.js
let decodeData = decodeURIComponent(totalData); //解決中文亂碼
write(path.join(__dirname, "/user.txt"), decodeData)
.then(res => {
response.end();
})
.catch(err => {
throw err;
});
也可以使用 async/await 的方法將 promise 的異步執行轉變為同步執行
request.on("end", async () => {
response.writeHead(200, { "Content-type": "text/html;charset=utf-8" });
let decodeData = decodeURIComponent(totalData); //解決中文亂碼
await write(path.join(__dirname, "/user.txt"), decodeData);
response.end();
});
此時可以使用 try...catch 進行錯誤捕獲了
try {
await write(path.join(__dirname, "/user.txt"), decodeData);
response.end();
} catch (err) {
console.log(err);
response.end();
}
為什么有同步方法了,還是要先將回調用 promise 包裝后再用 async/await 將其轉為同步呢?await 會阻塞 async 異步函數,但是並沒有阻塞主線程,async 本質上還是異步執行,只是看起來像是一個同步執行,我們可以繼續並行執行,而同步方法 sync 則不能並行執行.
追加內容 appendFile 和 appendFileSync
將數據追加(在最后接着寫入)到文件,如果文件尚不存在則創建該文件.data 可以是字符串或 Buffer.buffer 內容是十六進制信息的 ASCII 碼
和 writeFile 不同是,writeFile 也是將內容寫入文件,也是文件不存在就創建,但是文件存在的話,writeFile 寫入的內容會直接覆蓋原有內容,而 appendFile 是追加內容.所以新建內容還是 writeFile 比較好.
修改 file.js
append: function(filename, data, options) {
return new Promise((resolve, reject) => {
fs.appendFile(filename, data, options, err =>
err === null ? resolve(filename) : reject(err)
);
});
}
修改 app.js
let { write, append } = require("./file.js");
......
"/append": async (request, response) => {
response.writeHead(200, { "Content-type": "text/html;charset=utf-8" });
await append(
path.join(__dirname, "/user.txt"),
"我要向世界發出hello world"
);
response.end();
},
打開 user.txt 就發現文件的內容是 "username=我是好人&password=123456&remark=今天我要做一件事情我要向世界發出 hello world"
刪除文件 fs.unlink 和 fs.unlinkSync
修改 file.js
remove: function(path) {
return new Promise((resolve, reject) => {
fs.unlink(path, err => (err === null ? resolve(path) : reject(err)));
});
}
修改 app.js
"/append": async (request, response) => {
response.writeHead(200, { "Content-type": "text/html;charset=utf-8" });
await remove(path.join(__dirname, "/user.txt"));
await append(
path.join(__dirname, "/user.txt"),
"我要向世界發出hello world"
);
response.end();
},
這樣 user.txt 中內容只有"我要向世界發出 hello world"了,可以看出來是刪除文件后重新寫入的
文件重命名 fs.rename()
還是修改 file.js
rename: function(oldPath, newPath) {
return new Promise((resolve, reject) => {
fs.rename(oldPath, newPath, err =>
err === null ? resolve([oldPath, newPath]) : reject(err)
);
});
}
然后再 app.js 中執行
await rename(
path.join(__dirname, "/user.txt"),
path.join(__dirname, "/new_name.txt")
);
文件夾操作 mkdir,rmdir,readdir
mkdir 接受三個參數,第一個是目錄名,第二個是權限值,第三個是回調函數
readdir 方法用於讀取目錄,返回一個所包含的文件和子目錄的數組
rmdir 接收一個 path 參數用於刪除文件夾
新建 dir.js
let fs = require("fs");
module.exports = {
mkdir: function(path, options) {
return new Promise((resolve, reject) => {
fs.mkdir(path, options, err =>
err === null ? resolve(path) : reject(err)
);
});
},
readdir: function(path, options) {
return new Promise((resolve, reject) => {
fs.readdir(path, options, (err, files) =>
err === null ? resolve(files) : reject(err)
);
});
},
rmdir: function(path) {
return new Promise((resolve, reject) => {
fs.rmdir(path, err => (err === null ? resolve(path) : reject(err)));
});
}
還是在 app.js 中調用
"/dir": (request, response) => {
response.writeHead(200, { "Content-type": "text/html;charset=utf-8" });
["圖片", "文件", "書籍", "視頻"].forEach(async item => {
await mkdir(path.join(__dirname, `/${item}`));
await write(
path.join(__dirname, `/${item}/${item}.txt`),
"我還是要向世界發出hello world"
);
console.log("我在里面");
});
console.log("我在外面");
response.end();
},
文件夾此時創建成功了,控制台也會輸入"我在外面,我在里面,我在里面,我在里面,我在里面".
在使用同步寫一次
"/dir": (request, response) => {
response.writeHead(200, { "Content-type": "text/html;charset=utf-8" });
["圖片", "文件", "書籍", "視頻"].forEach(async item => {
fs.mkdirSync(path.join(__dirname, `/${item}`));
fs.writeFileSync(
path.join(__dirname, `/${item}/${item}.txt`),
"我還是要向世界發出hello world"
);
console.log("我在里面");
});
console.log("我在外面");
response.end();
},
然后控制台輸出是"我在里面,我在里面,我在里面,我在里面,我在外面",async/await 的同步終究是個異步,只是在代碼塊中執行像同步
文件夾操作 readdir
"/file": async (request, response) => {
response.writeHead(200, { "Content-type": "text/html;charset=utf-8" });
let dir_path = process.cwd();
let files = await readdir(dir_path);
let str = `<ul>${files
.map(item => {
return `<li><a href="/${item}">${item}</a></li>`;
})
.join("")}</ul>`;
response.write(str);
response.end();
},
文件夾操作 rmdir
空文件夾直接刪除
"/clear": async (request, response) => {
response.writeHead(200, { "Content-type": "text/html;charset=utf-8" });
["圖片", "文件", "書籍", "視頻"].forEach(async item => {
await rmdir(path.join(__dirname, `/${item}`));
});
response.end();
},
不為空的文件夾是無法直接刪除的,刪除的是文件而不是文件夾也會報錯.