您好,登錄后才能下訂單哦!
這篇文章給大家分享的是有關node.js文件操作系統的示例分析的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。
具體如下:
文件讀取
普通讀取
同步讀取
var fs = require('fs'); var data; try{ data = fs.readFileSync('./fileForRead.txt', 'utf8'); console.log('文件內容: ' + data); }catch(err){ console.error('讀取文件出錯: ' + err.message); }
輸出如下:
/usr/local/bin/node readFileSync.js
文件內容: hello world
異步讀取
var fs = require('fs'); fs.readFile('./fileForRead.txt', 'utf8', function(err, data){ if(err){ return console.error('讀取文件出錯: ' + err.message); } console.log('文件內容: ' + data); });
輸出如下
/usr/local/bin/node readFile.js
文件內容: hello world
通過文件流讀取
適合讀取大文件
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('已經關閉'); });
輸出如下
/usr/local/bin/node createReadStream.js
讀取數據: hello world
沒有數據了
已經關閉
文件寫入
備注:以下代碼,如果文件不存在,則創建文件;如果文件存在,則覆蓋文件內容;
異步寫入
var fs = require('fs'); fs.writeFile('./fileForWrite.txt', 'hello world', 'utf8', function(err){ if(err) throw err; console.log('文件寫入成功'); });
同步寫入
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]])
fd:寫入的文件句柄。
buffer:寫入的內容。
offset:將buffer從offset位置開始,長度為length的內容寫入。
length:寫入的buffer內容的長度。
position:從打開文件的position處寫入。
callback:參數為 (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)
fs.watchFile()
實現原理:輪詢。每隔一段時間檢查文件是否發生變化。所以在不同平臺上表現基本是一致的。
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()
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下可用。
問題來了:
不一致的表現。
不可用的場景。
linux上要recursive咋整。
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。
首先是方法
stats.isFile() -- 是否文件
stats.isDirectory() -- 是否目錄
stats.isBlockDevice() -- 什么鬼
stats.isCharacterDevice() -- 什么鬼
stats.isSymbolicLink() (only valid with fs.lstat()) -- 什么鬼
stats.isFIFO() -- 什么鬼
stats.isSocket() -- 是不是socket文件
官網例子:
{ 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 // 創建時間 }
atime:Access Time // 訪問時間
mtime:: Modified Time // 文件內容修改時間
ctime: Changed Time. // 文件狀態修改時間,比如修改文件所有者、修改權限、重命名等
birthtime: Birth Time // 創建時間。在某些系統上是不可靠的,因為拿不到。
例子:
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:49Process 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); }
文件打開/關閉
比較底層的接口,實際需要用到的機會不多。需要用到的時候看下文檔就行。
flags:文件打開模式,比如r、r+、w、w+等。可選模式非常多。
mode:默認是666,可讀+可寫。
fs.open(path, flags[, mode], callback) fs.openSync(path, flags[, mode]) fs.close(fd, callback) fs.closeSync(fd)
文件讀取(底層)
相對底層的讀取接口,參數如下
fd:文件句柄。
buffer:將讀取的文件內容寫到buffer里。
offset:buffer開始寫入的位置。(在offset開始寫入,還是offset+1?)
length:要讀取的字節數。
position:文件從哪個位置開始讀取。如果是null,那么就從當前位置開始讀取。(讀取操作會記錄下上一個位置)
此外,callback的回調參數為(err, bytesRead, buffer)
fs.read(fd, buffer, offset, length, position, callback)
追加文件內容
fs.appendFile(file, data[, options], callback)
file:可以是文件路徑,也可以是文件句柄。(還可以是buffer?)
data:要追加的內容。string或者buffer。
options
encoding:編碼,默認是utf8
mode:默認是0o666
flag:默認是a
注意:如果file是文件句柄,那么
開始追加數據前,file需要已經打開。
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.
修改文件屬性(時間)
path/fd:文件路徑/文件句柄
atime:Access Time。上一次訪問文件數據的時間。
mtime:Modified Time。修改時間。
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.
軟鏈接、硬鏈接區別:參考 或者 [這個]。(https://www.jb51.net/article/96135.htm)
硬鏈接:inode相同,多個別名。刪除一個硬鏈接文件,不會影響其他有相同inode的文件。
軟鏈接:有自己的inode,用戶數據塊存放指向文件的inode。
參考這里。
創建臨時目錄
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'); // fileForRealPath2.txt 是普通文件,正常運行 fs.realpath('./extra/inner/fileForRealPath2.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/fileForRealPath2.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)
可以參考這里:
1、sync函數 sync函數只是將所有修改過的塊緩沖區排入寫隊列,然后就返回,它并不等待實際寫磁盤操作結束。 通常稱為update的系統守護進程會周期性地(一般每隔30秒)調用sync函數。這就保證了定期沖洗內核的塊緩沖區。命令sync(1)也調用sync函數。
2、fsync函數 fsync函數只對由文件描述符filedes指定的單一文件起作用,并且等待寫磁盤操作結束,然后返回。 fsync可用于數據庫這樣的應用程序,這種應用程序需要確保將修改過的塊立即寫到磁盤上。
3、fdatasync函數 fdatasync函數類似于fsync,但它只影響文件的數據部分。而除數據外,fsync還會同步更新文件的屬性。 對于提供事務支持的數據庫,在事務提交時,都要確保事務日志(包含該事務所有的修改操作以及一個提交記錄)完全寫到硬盤上,才認定事務提交成功并返回給應用層。
待確認
通篇的mode,待確認。
fs.access()更多用法(涉及 fs.constants.F_OK等權限)
感謝各位的閱讀!關于“node.js文件操作系統的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。