基礎用法
在 node 中,使用 fs 模組來實現所有有關檔案以及目錄的建立,寫入和刪除操作。在 fs 模組中,所有的方法都分為同步和非同步兩種,==具有 sync 字尾的是同步方法==,不具有 sync 字尾的方法是非同步方法。
- 常用 api
fs.readFile
fs.writeFile
fs.copyFile
fs.unlink
fs.read
fs.write
fs.open
fs.sync
fs.close
fs.mkdir
fs.rmdir
fs.rname
fs.readdir
複製程式碼
文件地址:http://nodejs.cn/api/fs.html
命令列中輸入 ls -l
可以檢視檔案以及資料夾的許可權。
對檔案常見操作許可權是 666 即讀寫操作。如果想更改許可權 chmod -R 777 *
把所有檔案更改為最高許可權。助記:二爺一直死讀書(2寫1執4讀)
flag 引數含義
符號 | 含義 |
---|---|
r | 讀檔案,檔案不存在報錯 |
r+ | 讀取並寫入,檔案不存在報錯 |
rs | 同步讀取檔案並忽略快取 |
w | 寫入檔案,不存在則建立,存在則清空 |
wx | 排它寫入檔案 |
w+ | 讀取並寫入檔案,不存在則建立,存在則清空 |
wx+ | 和w+類似,排他方式開啟 |
a | 追加寫入 |
ax | 與a類似,排他方式寫入 |
a+ | 讀取並追加寫入,不存在則建立 |
ax+ | 作用與a+類似,但是以排他方式開啟檔案 |
助記:
- r 讀取
- w 寫入
- s 同步
- + 增加相反操作
- x 排他方式
- r+ w+的區別?
- 當檔案不存在時,r+不會建立,而會導致呼叫失敗,但w+會建立。
- 如果檔案存在,r+不會自動清空檔案,但w+會自動把已有檔案的內容清空。
檔案操作
- fs.readFile 讀取檔案
fs.readFile(__dirname + '/a.text',{ encoding: 'utf8', flag: 'r' }, function(err, data) {
if (err) {
console.log(err);
return;
}
console.log(data);
});
複製程式碼
- fs.writeFile 寫入檔案
// 檔案裡存的永遠是二進位制
fs.writeFile(__dirname + '/a.text', Buffer.from('1233333'), { flag: 'w', mode: 0o666 }, function(error, data) {
console.log(error);
});
複製程式碼
- fs.copyFile 拷貝檔案
fs.copyFile(__dirname + '/buffer.js', __dirname + '/a.text', function() {
console.log('成功');
})
複製程式碼
注意: 如果要實現拷貝大檔案,以上操作都會造成記憶體過大情況
- fs.open && fs.read
fd 檔案描述符由3開始。0 標準輸入 1 標準輸出 2 錯誤輸出
fs.open(__dirname + '/a.text', 'r', function(err, fd ) {
let BUFFER_SIZE = 3;
// 把檔案的內容讀取到記憶體中。
let buffer = Buffer.alloc(BUFFER_SIZE);
// fd 描述符
// buffer 讀取到哪個buffer上
// offset buffer的偏移量
// byteRead 實際讀到的個數
fs.read(fd, buffer, 0, BUFFER_SIZE, 0, function(err, byteRead) {
console.log(buffer);
});
});
複製程式碼
每次讀3個迴圈讀直到讀完為止,讀取完畢後將其關閉。
fs.open(__dirname + '/a.text', 'r', function(err, fd ) {
let BUFFER_SIZE = 3;
// 把檔案的內容讀取到記憶體中。
let buffer = Buffer.alloc(BUFFER_SIZE);
let index = 0;
// fd 描述符
// buffer 讀取到哪個buffer上
// offset buffer的偏移量
// byteRead 實際讀到的個數
function next() {
fs.read(fd, buffer, 0, BUFFER_SIZE, index, function(err, byteRead) {
index += byteRead;
console.log(buffer.slice(0, byteRead).toString());
if (byteRead === BUFFER_SIZE) {
next();
} else {
fs.close(fd, () => {
console.log('關閉');
})
}
});
}
next();
});
複製程式碼
邊讀取邊寫入,1.text 拷貝到 5.text中
// 1.text 拷貝到 5.text中
function copy(source, target) {
let BUFFER_SIZE = 3;
let index = 0;
let buffer = Buffer.alloc(BUFFER_SIZE);
// 讀取檔案
fs.open(source, 'r', function(error, rfd) {
if (error) {
console.log(error);
return;
}
// 寫入檔案
fs.open(target, 'w', 0o666, function(err, wfd) {
function next() {
fs.read(rfd, buffer, 0, BUFFER_SIZE, index, function(er, bytesRead) {
// 要寫入的檔案描述符、寫入的buffer、buffer的偏移量、buffer寫入的個數、檔案的位置、callback
fs.write(wfd, buffer, 0, bytesRead, index, function(e, byteWritten) {
console.log(byteWritten);
index += bytesRead;
// 如果還有要寫入的內容就繼續讀取
if (byteWritten) {
next();
} else {
// 關閉檔案
fs.close(rfd, () => {});
// 把記憶體中的內容強制寫完後在關閉檔案。(寫入的操作是非同步操作)
fs.fsync(function() {
fs.close(wfd, () => {});
});
}
})
});
}
next();
});
})
}
copy('1.text', '5.text');
複製程式碼
資料夾操作
建立目錄(同步和非同步),非同步不會阻塞主執行緒
fs.mkdirSync('a');
// 不能跨級建立 如 a/b/c
複製程式碼
- fs.mkdirSync && fs.mkdir
同步方法實現建立資料夾 /a/b/c/d/e/f
function makep(p) { // a/b/c
let dirs = p.split('/');
console.log(dirs);
for(let i = 0; i < dirs.length; i++) {
let p = dirs.slice(0, i+1).join('/');
// 判斷當前檔案是否存在
try {
fs.accessSync(p)
} catch(e) {
fs.mkdirSync(p);
}
}
}
makep('a/b/c/d/e/f');
複製程式碼
非同步方法實現建立資料夾 /a/b/c/d/e/f
function makep(dir, callback) {
let dirs = dir.split('/');
let index = 1;
function next(index) {
// 當索引溢位時候停止遞迴
if(index - 1 === dirs.length) {
return callback();
}
let p = dirs.slice(0, index).join('/');
fs.access(p, (err)=>{
if(!err) {
// 沒錯誤表示存在當前目錄則建立下一個目錄
next(index + 1)
} else {
// 如果沒有這個檔案,報錯,則建立這個檔案,建立完後建立下一個檔案
fs.mkdir(p, (err) => {
if (err) return console.log(err);
next(index + 1);
});
}
})
}
next(index);
}
makep('a/b/c/d/e/f', () => {
// 建立完成回撥
console.log('ok');
});
複製程式碼
- fs.rmdirSync
刪除檔案首先保證檔案為空。跟建立檔案相反。
遍歷的順序: 先序、中序、後序
- 先序:先遍歷 b 再 c 再 d
- 中序:先遍歷 c 再 b 再 d
- 後序:先遍歷 c 再 d 在 b
主要看 b 的位置。遍歷也包括深度和廣度
- fs.unlink 刪除檔案
只有一層目錄情況
// 讀取目錄 一級
let dirs = fs.readdirSync('c'); // [ 'a', 'a.js' ]
// 對映路徑[ 'c/a', 'c/a.js' ]
dirs = dirs.map(item => {
return path.join('c', item);
});
dirs.forEach(p => {
// 判斷檔案的狀態 stat 上有兩個方法 isDirectory isFile
let stat = fs.statSync(p);
if (stat.isDirectory()) { // 是否是資料夾
fs.rmdirSync(p);
} else {
// 刪除檔案
fs.unlinkSync(p);
}
});
複製程式碼
刪除多層目錄
- 遞迴同步深度刪除
// 深度刪除由內到外 遞迴深度刪除
function removeDirSync(dir) {
// 刪除的時候允許 刪除的不一定是目錄
let stat = fs.statSync(dir);
if (stat.isDirectory()) { // 資料夾 讀取檔案中內容
// 讀取目錄下檔案
let dirs = fs.readdirSync(dir);
// 拼接目錄
dirs = dirs.map(d => path.join(dir, d)); // [c/a, c/b]
console.log(dirs);
dirs.forEach(d => { // 依次刪除子目錄
// 遞迴呼叫刪除目錄裡檔案
removeDirSync(d);
});
// 刪除自己
fs.rmdirSync(dir);
} else { // 檔案 刪除跑路
fs.unlinkSync(dir);
}
}
removeDirSync('c');
複製程式碼
- 遞迴非同步深度刪除 - promise
function removeDir(dir) {
return new Promise((resolve, reject) => {
// 判斷檔案型別
fs.stat(dir, (err, stat) => {
if (stat.isDirectory()) {
// 讀取當前目錄下的內容
fs.readdir(dir, (err, dirs) => {
// 拼接路徑 [c/b, c/d]
dirs = dirs.map(d => path.join(dir, d));
// promise 化
dirs = dirs.map(p => {
return removeDir(p); // 【promise, promise】
});
Promise.all(dirs).then(() => {
console.log(dir, 111);
// 刪除自己
fs.rmdir(dir, resolve);
});
});
} else {
// 如果是檔案刪除後,呼叫 promise 成功
fs.unlink(dir, resolve);
}
});
});
}
removeDir('b').then(data=>{
console.log('ok');
});
複製程式碼
- 普通回撥深度刪除
// 普通回撥刪除
function rmdir(dir, callback) {
fs.stat(dir, (err, stat) => {
if (stat.isDirectory()) {
// 目錄
fs.readdir(dir, (err, dirs) => {
// 只要涉及到非同步遞迴 使用next
function next(index) {
if ((dirs.length === 0) || index === dirs.length) {
return fs.rmdir(dir, callback); // 目錄下沒內容刪除自己
}
let p = path.join(dir, dirs[index]); // a/b
rmdir(p, ()=>next(index+1));// 刪除 a/b 後刪除 a/c
}
next(0);
});
} else {
// 檔案
fs.unlink(dir, callback);
}
})
}
rmdir('a', function() {
console.log('ok');
});
複製程式碼
- 廣度刪除
先準備一個陣列。改變指標移動,將目錄讀出放到陣列中。字典刪除
同步方式:由後向前刪除
function preWide(dir) {
let arr = [dir];
// 指標
let index = 0;
while(arr[index]) {
let current = arr[index++];
let stat = fs.statSync(current);
if (stat.isDirectory()) {
// 讀取內容
let dirs = fs.readdirSync(current);
arr = [...arr, ...dirs.map(d => path.join(current, d))];
}
}
console.log(arr); // [ 'a', 'a/a.js', 'a/b', 'a/c', 'a/b/b.js', 'a/b/d', 'a/c/c.js' ]
for (let i = arr.length - 1; i >=0 ; i--) {
let p = arr[i];
let stat = fs.statSync(p);
if(stat.isDirectory()) {
fs.rmdirSync(p);
} else {
fs.unlinkSync(p);
}
}
}
preWide('a');
複製程式碼