同步讀取html
var fs = require('fs'); var data; try{ data = fs.readFileSync('./fileForRead.txt', 'utf8'); console.log('文件內容: ' + data); }catch(err){ console.error('讀取文件出錯: ' + err.message); }
輸出以下:node
/usr/local/bin/node readFileSync.js 文件內容: hello world
異步讀取linux
var fs = require('fs'); fs.readFile('./fileForRead.txt', 'utf8', function(err, data){ if(err){ return console.error('讀取文件出錯: ' + err.message); } console.log('文件內容: ' + data); });
輸出以下git
/usr/local/bin/node readFile.js 文件內容: hello world
適合讀取大文件github
var fs = require('fs'); var readStream = fs.createReadStream('./fileForRead.txt', 'utf8'); readStream .on('data', function(chunk) { console.log('讀取數據: ' + chunk); }) .on('error', function(err){ console.log('出錯: ' + err.message); }) .on('end', function(){ // 沒有數據了 console.log('沒有數據了'); }) .on('close', function(){ // 已經關閉,不會再有事件拋出 console.log('已經關閉'); });
輸出以下shell
/usr/local/bin/node createReadStream.js 讀取數據: hello world 沒有數據了 已經關閉
備註:如下代碼,若是文件不存在,則建立文件;若是文件存在,則覆蓋文件內容;數據庫
異步寫入windows
var fs = require('fs'); fs.writeFile('./fileForWrite.txt', 'hello world', 'utf8', function(err){ if(err) throw err; console.log('文件寫入成功'); });
同步寫入api
var fs = require('fs'); try{ fs.writeFileSync('./fileForWrite1.txt', 'hello world', 'utf8'); console.log('文件寫入成功'); }catch(err){ throw err; }
var fs = require('fs'); var writeStream = fs.createWriteStream('./fileForWrite1.txt', 'utf8'); writeStream .on('close', function(){ // 已經關閉,不會再有事件拋出 console.log('已經關閉'); }); writeStream.write('hello'); writeStream.write('world'); writeStream.end('');
fs.write(fd, buffer, offset, length[, position], callback) fs.write(fd, data[, position[, encoding]], callback) fs.writeSync(fd, buffer, offset, length[, position]) fs.writeSync(fd, data[, position[, encoding]])網絡
(err, written, buffer)
。written
表示有xx字節的buffer被寫入。備註:fs.write(fd, buffer, offset, length[, position], callback)
跟fs.write(fd, data[, position[, encoding]], callback)
的區別在於:後面的只能把全部的data寫入,而前面的能夠寫入指定的data子串?
fs.exists()
已是deprecated
狀態,如今能夠經過下面代碼判斷文件是否存在。
var fs = require('fs'); fs.access('./fileForRead.txt', function(err){ if(err) throw err; console.log('fileForRead.txt存在'); }); fs.access('./fileForRead2.txt', function(err){ if(err) throw err; console.log('fileForRead2.txt存在'); });
fs.access()
除了判斷文件是否存在(默認模式),還能夠用來判斷文件的權限。
備忘:fs.constants.F_OK
等常量沒法獲取(node v6.1,mac 10.11.4下,fs.constants
是undefined
)
異步版本(若是目錄已存在,會報錯)
var fs = require('fs'); fs.mkdir('./hello', function(err){ if(err) throw err; console.log('目錄建立成功'); });
同步版本
var fs = require('fs'); fs.mkdirSync('./hello');
var fs = require('fs'); fs.unlink('./fileForUnlink.txt', function(err){ if(err) throw err; console.log('文件刪除成功'); });
var fs = require('fs'); fs.unlinkSync('./fileForUnlink.txt');
// fs.mkdir(path[, mode], callback) var fs = require('fs'); fs.mkdir('sub', function(err){ if(err) throw err; console.log('建立目錄成功'); });
// fs.mkdirSync(path[, mode]) var fs = require('fs'); try{ fs.mkdirSync('hello'); console.log('建立目錄成功'); }catch(e){ throw e; }
同步版本,注意:fs.readdirSync()
只會讀一層,因此須要判斷文件類型是否目錄,若是是,則進行遞歸遍歷。
// fs.readdirSync(path[, options]) var fs = require('fs'); var path = require('path'); var getFilesInDir = function(dir){ var results = [ path.resolve(dir) ]; var files = fs.readdirSync(dir, 'utf8'); files.forEach(function(file){ file = path.resolve(dir, file); var stats = fs.statSync(file); if(stats.isFile()){ results.push(file); }else if(stats.isDirectory()){ results = results.concat( getFilesInDir(file) ); } }); return results; }; var files = getFilesInDir('../'); console.log(files);
異步版本:(TODO)
// fs.rename(oldPath, newPath, callback) var fs = require('fs'); fs.rename('./hello', './world', function(err){ if(err) throw err; console.log('重命名成功'); });
fs.renameSync(oldPath, newPath) var fs = require('fs'); fs.renameSync('./world', './hello');
fs.watch()
比fs.watchFile()
高效不少(why)
實現原理:輪詢。每隔一段時間檢查文件是否發生變化。因此在不一樣平臺上表現基本是一致的。
var fs = require('fs'); var options = { persistent: true, // 默認就是true interval: 2000 // 多久檢查一次 }; // curr, prev 是被監聽文件的狀態, fs.Stat實例 // 能夠經過 fs.unwatch() 移除監聽 fs.watchFile('./fileForWatch.txt', options, function(curr, prev){ console.log('修改時間爲: ' + curr.mtime); });
修改fileForWatch.txt
,能夠看到控制檯下打印出日誌
/usr/local/bin/node watchFile.js 修改時間爲: Sat Jul 16 2016 19:03:57 GMT+0800 (CST) 修改時間爲: Sat Jul 16 2016 19:04:05 GMT+0800 (CST)
爲啥子?莫非單純訪問文件也會觸發回調?
If you want to be notified when the file was modified, not just accessed, you need to compare curr.mtime and prev.mtime.
在 v0.10 以後的改動。若是監聽的文件不存在,會怎麼處理。以下
Note: when an fs.watchFile operation results in an ENOENT error, it will invoke the listener once, with all the fields zeroed (or, for dates, the Unix Epoch). In Windows, blksize and blocks fields will be undefined, instead of zero. If the file is created later on, the listener will be called again, with the latest stat objects. This is a change in functionality since v0.10.
fs.watch(filename[, options][, listener]) fs.unwatchFile(filename[, listener])
這接口很是不靠譜(當前測試用的v6.1.0),參考 https://github.com/nodejs/node/issues/7420
fs.watch(filename[, options][, listener])#
注意:fs.watch()
這個接口並非在全部的平臺行爲都一致,而且在某些狀況下是不可用的。recursive
這個選項只在mac
、windows
下可用。
問題來了:
The fs.watch API is not 100% consistent across platforms, and is unavailable in some situations. The recursive option is only supported on OS X and Windows.
備忘,不可用的場景。好比網絡文件系統等。
For example, watching files or directories can be unreliable, and in some cases impossible, on network file systems (NFS, SMB, etc), or host file systems when using virtualization software such as Vagrant, Docker, etc.
另外,listener回調有兩個參數,分別是event
、filename
。其中,filename
僅在linux、windows上會提供,而且不是100%提供,因此,儘可能不要依賴filename
。
在linux、osx上,fs.watch()
監聽的是inode。若是文件被刪除,並從新建立,那麼刪除事件會觸發。同時,fs.watch()
監聽的仍是最初的inode。(API的設計就是這樣的)
結論:怎麼看都感受這個API很不靠譜,雖然性能比fs.watchFile()要高不少。
先來個例子,在osx下測試了一下,簡直使人絕望。。。不管是建立、修改、刪除文件,evt
都是rename
。。。
var fs = require('fs'); var options = { persistent: true, recursive: true, encoding: 'utf8' }; fs.watch('../', options, function(event, filename){ console.log('觸發事件:' + event); if(filename){ console.log('文件名是: ' + filename); }else{ console.log('文件名是沒有提供'); } });
修改下fileForWatch.txt
,看到下面輸出。。。感受打死也不想用這個API。。。
貼下環境:osx 10.11.4, node v6.1.0。
觸發事件:rename
文件名是: fs/fileForWatch.txt___jb_bak___
觸發事件:rename
文件名是: fs/fileForWatch.txt 觸發事件:rename 文件名是: fs/fileForWatch.txt___jb_old___ 觸發事件:rename 文件名是: .idea/workspace.xml___jb_bak___ 觸發事件:rename 文件名是: .idea/workspace.xml 觸發事件:rename 文件名是: .idea/workspace.xml___jb_old___
參考linux命令行,不舉例子了。。。
fs.chown(path, uid, gid, callback) fs.chownSync(path, uid, gid) fs.fchown(fd, uid, gid, callback) fs.fchownSync(fd, uid, gid)
能夠用fs.chmod()
,也能夠用fs.fchmod()
。二者的區別在於,前面傳的是文件路徑,後面傳的的文件句柄。
fs.chmod)
、fs.fchmod()
區別:傳的是文件路徑,仍是文件句柄。fs.chmod()
、fs.lchmod()
區別:若是文件是軟鏈接,那麼fs.chmod()
修改的是軟鏈接指向的目標文件;fs.lchmod()
修改的是軟鏈接。fs.chmod(path, mode, callback) fs.chmodSync(path, mode)
fs.fchmod(fd, mode, callback) fs.fchmodSync(fd, mode)
fs.lchmod(path, mode, callback)# fs.lchmodSync(path, mode)
例子:
var fs = require('fs'); fs.chmod('./fileForChown.txt', '777', function(err){ if(err) console.log(err); console.log('權限修改爲功'); });
同步版本:
var fs = require('fs'); fs.chmodSync('./fileForChown.txt', '777');
區別:
fs.stat()
vs fs.fstat()
:傳文件路徑 vs 文件句柄。fs.stat()
vs fs.lstat()
:若是文件是軟連接,那麼fs.stat()
返回目標文件的狀態,fs.lstat()
返回軟連接自己的狀態。fs.stat(path, callback) fs.statSync(path)
fs.fstat(fd, callback) fs.fstatSync(fd)
fs.lstat(path, callback) fs.lstatSync(path)
主要關注Class: fs.Stats
。
首先是方法
官網例子:
{
dev: 2114, ino: 48064969, mode: 33188, nlink: 1, uid: 85, gid: 100, rdev: 0, size: 527, blksize: 4096, blocks: 8, atime: Mon, 10 Oct 2011 23:24:11 GMT, // 訪問時間 mtime: Mon, 10 Oct 2011 23:24:11 GMT, // 文件內容修改時間 ctime: Mon, 10 Oct 2011 23:24:11 GMT, // 文件狀態修改時間 birthtime: Mon, 10 Oct 2011 23:24:11 GMT // 建立時間 }
例子:
var fs = require('fs'); var getTimeDesc = function(d){ return [d.getFullYear(), d.getMonth()+1, d.getDate()].join('-') + ' ' + [d.getHours(), d.getMinutes(), d.getSeconds()].join(':'); }; fs.stat('./fileForStat.txt', function(err, stats){ console.log('文件大小: ' + stats.size); console.log('建立時間: ' + getTimeDesc(stats.birthtime)); console.log('訪問時間: ' + getTimeDesc(stats.atime)); console.log('修改時間: ' + getTimeDesc(stats.mtime)); });
輸出以下:
/usr/local/bin/node stat.js 文件大小: 3613 建立時間: 2016-7-16 12:40:49 訪問時間: 2016-7-16 12:40:49 修改時間: 2016-7-16 12:40:49 Process finished with exit code 0
同步的例子:
var fs = require('fs'); var getTimeDesc = function(d){ return [d.getFullYear(), d.getMonth()+1, d.getDate()].join('-') + ' ' + [d.getHours(), d.getMinutes(), d.getSeconds()].join(':'); }; var stats = fs.statSync('./fileForStat.txt'); console.log('文件大小: ' + stats.size); console.log('建立時間: ' + getTimeDesc(stats.birthtime)); console.log('訪問時間: ' + getTimeDesc(stats.atime)); console.log('修改時間: ' + getTimeDesc(stats.mtime));
例子:
// fs.access(path[, mode], callback) var fs = require('fs'); fs.access('./fileForAccess.txt', function(err){ if(err) throw err; console.log('能夠訪問'); });
同步版本:
// fs.accessSync(path[, mode]) var fs = require('fs'); // 若是成功,則返回undefined,若是失敗,則拋出錯誤(什麼鬼) try{ fs.accessSync('./fileForAccess.txt'); }catch(e){ throw(e); }
比較底層的接口,實際須要用到的機會很少。須要用到的時候看下文檔就行。
r
、r+
、w
、w+
等。可選模式很是多。666
,可讀+可寫。fs.open(path, flags[, mode], callback) fs.openSync(path, flags[, mode]) fs.close(fd, callback) fs.closeSync(fd)
相對底層的讀取接口,參數以下
此外,callback
的回調參數爲(err, bytesRead, buffer)
fs.read(fd, buffer, offset, length, position, callback)
fs.appendFile(file, data[, options], callback)
utf8
0o666
a
注意:若是file
是文件句柄,那麼
var fs = require('fs'); fs.appendFile('./extra/fileForAppend.txt', 'helo', 'utf8', function(err){ if(err) throw err; console.log('append成功'); });
fs.truncate(path, len, callback) fs.truncateSync(path, len)
fs.ftruncate(fd, len, callback) fs.ftruncateSync(fd, len)
用途參考linux說明文檔。
要點:
offset
不會變化。好比經過fs.read()
讀取文件內容,就須要特別注意。len
小於文件內容長度,剩餘文件內容部分會丟失;若是len
大於文件內容長度,那麼超出的部分,會用\0
進行填充。The truncate() and ftruncate() functions cause the regular file named by path or referenced by fd to be truncated to a size of precisely length bytes.
If the file previously was larger than this size, the extra data is lost. If the file previously was shorter, it is extended, and the extended part reads as null bytes ('\0').
The file offset is not changed.
With ftruncate(), the file must be open for writing; with truncate(), the file must be writable.
fs.utimes(path, atime, mtime, callback) fs.utimesSync(path, atime, mtime)
fs.futimes(fd, atime, mtime, callback) fs.futimesSync(fd, atime, mtime)
備註,在命令行下能夠
stat
查看文件的狀態信息,包括了上面的atime、mtime。touch
修改這幾個時間。fs.symlink(target, path[, type], callback) fs.symlinkSync(target, path[, type])
fs.link(srcpath, dstpath, callback) fs.linkSync(srcpath, dstpath)
link() creates a new link (also known as a hard link) to an existing file.
軟連接、硬連接區別:參考 或者 [這個]。(http://www.cnblogs.com/itech/archive/2009/04/10/1433052.html)
參考這裏。
fs.mkdtemp(prefix, callback) fs.mkdtempSync(prefix)
備忘:跟普通的隨便找個目錄,建立個隨機名字的文件夾,有什麼區別?
代碼示例以下:
var fs = require('fs'); fs.mkdtemp('/tmp/', function(err, folder){ if(err) throw err; console.log('建立臨時目錄: ' + folder); });
輸出以下:
/usr/local/bin/node mkdtemp.js 建立臨時目錄: /tmp/Cxw51O
fs.readlink(path[, options], callback) fs.readlinkSync(path[, options])
以下面例子,建立了個軟連接指向fileForReadLink.txt
,經過fs.readlink()
就能夠找出原始的路徑。
var fs = require('fs'); var randomFileName = './extra/fileForReadLink-' + String(Math.random()).slice(2, 6) + '.txt'; fs.symlinkSync('./extra/fileForReadLink.txt', randomFileName); fs.readlink(randomFileName, 'utf8', function(err, linkString){ if(err) throw err; console.log('連接文件內容: ' + linkString); });
相似終端下直接運行readlink
。對於軟連接文件,效果同上面代碼。對於硬連接,沒有輸出。
➜ extra git:(master) ✗ readlink fileForReadLink-9827.txt ./extra/fileForReadLink.txt ➜ extra git:(master) ✗ readlink fileForLinkHard.txt ➜ extra git:(master) ✗ readlink fileForLinkSoft.txt ./extra/fileForLink.txt
fs.realpath(path[, options], callback) fs.realpathSync(path[, options])
例子:(不能做用於軟連接?)
var fs = require('fs'); var path = require('path'); // fileForRealPath1.txt 是普通文件,正常運行 fs.realpath('./extra/inner/fileForRealPath1.txt', function(err, resolvedPath){ if(err) throw err; console.log('fs.realpath: ' + resolvedPath); }); // fileForRealPath.txt 是軟連接, 會報錯,提示找不到文件 fs.realpath('./extra/inner/fileForRealPath.txt', function(err, resolvedPath){ if(err) throw err; console.log('fs.realpath: ' + resolvedPath); }); console.log( 'path.resolve: ' + path.resolve('./extra/inner/fileForRealpath.txt') );
輸出以下:
path.resolve: /Users/a/Documents/git-code/git-blog/demo/2015.05.21-node-basic/fs/extra/inner/fileForRealpath.txt fs.realpath: /Users/a/Documents/git-code/git-blog/demo/2015.05.21-node-basic/fs/extra/inner/fileForRealPath1.txt /Users/a/Documents/git-code/git-blog/demo/2015.05.21-node-basic/fs/realpath.js:12 if(err) throw err; ^ Error: ENOENT: no such file or directory, realpath './extra/inner/fileForRealPath.txt' at Error (native) Process finished with exit code 1
fs.rmdir(path, callback) fs.rmdirSync(path)
例子以下:
var fs = require('fs'); fs.rmdir('./dirForRemove', function(err){ if(err) throw err; console.log('目錄刪除成功'); });
fs.fdatasync(fd, callback) fs.fdatasyncSync(fd)
能夠參考這裏:
一、sync函數 sync函數只是將全部修改過的塊緩衝區排入寫隊列,而後就返回,它並不等待實際寫磁盤操做結束。 一般稱爲update的系統守護進程會週期性地(通常每隔30秒)調用sync函數。這就保證了按期沖洗內核的塊緩衝區。命令sync(1)也調用sync函數。 二、fsync函數 fsync函數只對由文件描述符filedes指定的單一文件起做用,而且等待寫磁盤操做結束,而後返回。 fsync可用於數據庫這樣的應用程序,這種應用程序須要確保將修改過的塊當即寫到磁盤上。 三、fdatasync函數 fdatasync函數相似於fsync,但它隻影響文件的數據部分。而除數據外,fsync還會同步更新文件的屬性。 對於提供事務支持的數據庫,在事務提交時,都要確保事務日誌(包含該事務全部的修改操做以及一個提交記錄)徹底寫到硬盤上,才認定事務提交成功並返回給應用層。
mode
,待確認。