node.js同步/異步文件讀寫-fs,Stream文件流操作實例詳解
文件讀寫——fs
nodejs文檔里api一大堆,該怎么選擇呢?我這里挑選了幾個常用的出來。
案例說明:當(dāng)前目錄下有index.js和1.txt兩個文件
fs 模塊提供了一些 API,用于以一種類似標(biāo)準(zhǔn) POSIX 函數(shù)的方式與文件系統(tǒng)進(jìn)行交互。
const fs = require('fs');
- 異步形式的最后一個參數(shù)都是完成時回調(diào)函數(shù)。 傳給回調(diào)函數(shù)的參數(shù)取決于具體方法,但回調(diào)函數(shù)的第一個參數(shù)都會保留給異常。 如果操作成功完成,則第一個參數(shù)會是 null 或 undefined。
const fs = require('fs'); fs.unlink('/tmp/hello', (err) => { if (err) throw err; console.log('成功刪除 /tmp/hello'); });
- 當(dāng)使用同步操作時,任何異常都會被立即拋出,可以使用 try/catch 來處理異常,或讓異常向上冒泡。
const fs = require('fs'); try { fs.unlinkSync('/tmp/hello'); console.log('successfully deleted /tmp/hello'); } catch (err) { // handle the error }
異步的文件讀取
- 我們先讀取一下這個文件:
const fs = require("fs"); fs.readFile("./1.txt",(err,data)=>{ console.log(data); })
我們怎么知道它正確讀取到了呢?一堆16進(jìn)制的buffer.
readFile接收三個參數(shù),必選參數(shù):路徑,可選參數(shù):編碼,必選參數(shù):回調(diào)函數(shù)。
const fs = require("fs"); fs.readFile("./1.txt","utf-8",(err,data)=>{ console.log(data); })
- 注意兩個異步不能相干:
const fs = require("fs"); const fn = async () => { const data = await new Promise((resovle,reject) => { fs.readFile('1.txt','utf-8',(err,data) => { if(err) return reject(err) resovle(data) }) }) console.log(data,111); } console.log(1); console.log(fn(),222); console.log(2);
顯然異步讀取數(shù)據(jù)不能這樣做,人家本身就是異步的,你還拿異步的去取,肯定靠天吃飯啦。
想要準(zhǔn)確的拿數(shù)據(jù),還是得遵守async函數(shù)的規(guī)則,乖乖在它的懷抱下等待。
同步的文件讀取
- fs.readFileSync(‘路徑’,‘編碼’)
const fs = require("fs"); console.log(1); const data = fs.readFileSync('1.txt','utf-8') console.log(data); console.log(2);
很愉快的讀取到數(shù)據(jù),因為是同步,所以后面的代碼必須等待前面讀取文件成功才能繼續(xù)執(zhí)行。如果文件非常大,所以還是乖乖異步吧!
異步的寫文件
fs.writeFile(file, data[, options])
- fs.writeFile,第一個參數(shù)為路徑和文件,第二個參數(shù)為寫入的數(shù)據(jù),第三個會回執(zhí)消息,如果沒有錯誤會返回null:
如果你把1.txt刪了或者路徑下沒有這個文件的話會自動創(chuàng)建文件
const fs = require("fs"); console.log(1); const data = '寫入測試文字' fs.writeFile('1.txt',data,res=>{ console.log(res,11111); }) console.log(2);
但是如果僅是這樣的話,會把原來的文字覆蓋。
同步的寫文件
fs.writeFileSync(file, data[, options])
const fs = require("fs"); console.log(1); const data = '寫入測試文字' fs.writeFileSync('1.txt', data, res => { console.log(res, 11111); }) console.log(2);
因為這個寫函數(shù)如果沒有對應(yīng)文件會自己新建,所以想報錯還真的很難!暫時沒有錯誤示例!
open,打開文件
fs.open(path, flags[, mode], callback)
四個參數(shù),mode是默認(rèn)的:
- path
<string>
|<Buffer>
|<URL>
- flags
<string>
|<number>
- mode
<integer>
Default: 0o666 - callback
<Function>
- err
<Error>
- fd
<integer>
- err
具體的參數(shù):
flags 可以是:
‘r’ - 以讀取模式打開文件。如果文件不存在則發(fā)生異常。
‘r+’ - 以讀寫模式打開文件。如果文件不存在則發(fā)生異常。
‘rs+’ - 以同步讀寫模式打開文件。命令操作系統(tǒng)繞過本地文件系統(tǒng)緩存。
這對 NFS 掛載模式下打開文件很有用,因為它可以讓你跳過潛在的舊本地緩存。 它對 I/O 的性能有明顯的影響,所以除非需要,否則不要使用此標(biāo)志。
注意,這不會使 fs.open() 進(jìn)入同步阻塞調(diào)用。 如果那是你想要的,則應(yīng)該使用 fs.openSync()。
‘w’ - 以寫入模式打開文件。文件會被創(chuàng)建(如果文件不存在)或截斷(如果文件存在)。
‘wx’ - 類似 ‘w’,但如果 path 存在,則失敗。
‘w+’ - 以讀寫模式打開文件。文件會被創(chuàng)建(如果文件不存在)或截斷(如果文件存在)。
‘wx+’ - 類似 ‘w+’,但如果 path 存在,則失敗。
‘a’ - 以追加模式打開文件。如果文件不存在,則會被創(chuàng)建。
‘ax’ - 類似于 ‘a’,但如果 path 存在,則失敗。
‘a+’ - 以讀取和追加模式打開文件。如果文件不存在,則會被創(chuàng)建。
‘ax+’ - 類似于 ‘a+’,但如果 path 存在,則失敗。
mode 可設(shè)置文件模式(權(quán)限和 sticky 位),但只有當(dāng)文件被創(chuàng)建時才有效。默認(rèn)為 0o666,可讀寫。
- 該回調(diào)有兩個參數(shù) (err, fd)。
特有的標(biāo)志 ‘x’(在 open(2) 中的 O_EXCL 標(biāo)志)確保 path 是新創(chuàng)建的。 在 POSIX 操作系統(tǒng)中,path 會被視為存在,即使是一個鏈接到一個不存在的文件的符號。 該特有的標(biāo)志有可能在網(wǎng)絡(luò)文件系統(tǒng)中無法使用。
flags 也可以是一個數(shù)字,[open(2)] 文檔中有描述; 常用的常量可從 fs.constants 獲取。 在 Windows 系統(tǒng)中,標(biāo)志會被轉(zhuǎn)換為與它等同的替代者,例如,O_WRONLY 轉(zhuǎn)換為 FILE_GENERIC_WRITE、或 O_EXCL|O_CREAT 轉(zhuǎn)換為 CREATE_NEW,通過 CreateFileW 接受。
在 Linux 中,當(dāng)文件以追加模式打開時,定位的寫入不起作用。 內(nèi)核會忽略位置參數(shù),并總是附加數(shù)據(jù)到文件的末尾。
callback[err,fa]
失敗就不說了,讀取成功后,fd將是讀取文件中read方法的第一個參數(shù),這個參數(shù)代表指定的文件。
注意:fs.open() 某些標(biāo)志的行為是與平臺相關(guān)的。 因此,在 macOS 和 Linux 下用 ‘a+’ 標(biāo)志打開一個目錄(見下面的例子),會返回一個錯誤。 與此相反,在 Windows 和 FreeBSD,則會返回一個文件描述符。
// macOS 與 Linux fs.open('<directory>', 'a+', (err, fd) => { // => [Error: EISDIR: illegal operation on a directory, open <directory>] }); // Windows 與 FreeBSD fs.open('<directory>', 'a+', (err, fd) => { // => null, <fd> });
fs.read讀文件,基于open
參數(shù)
- fd
<integer>
- buffer
<Buffer>
|<Uint8Array>
- offset
<integer>
- length
<integer>
- position
<integer>
- callback
<Function>
- err
<Error>
- bytesRead
<integer>
- buffer
<Buffer>
- err
參數(shù)的值
從 fd 指定的文件中讀取數(shù)據(jù)。
buffer 是數(shù)據(jù)將被寫入到的 buffer。
offset 是 buffer 中開始寫入的偏移量。
length 是一個整數(shù),指定要讀取的字節(jié)數(shù)。
position 指定從文件中開始讀取的位置。 如果 position 為 null,則數(shù)據(jù)從當(dāng)前文件讀取位置開始讀取,且文件讀取位置會被更新。 如果 position 為一個整數(shù),則文件讀取位置保持不變。
回調(diào)有三個參數(shù) (err, bytesRead, buffer)。
如果調(diào)用該方法的 util.promisify() 版本,將會返回一個包含 bytesRead 和 buffer 屬性的 Promise。
例子:
- 使用read要和open結(jié)合在一起,通過open返回的fd指定文件拿到要讀取的目標(biāo)!
- 注意nodejs的buffer已經(jīng)廢棄了很多api!
var fs = require('fs'); fs.open('./1.txt', 'r', function (err, fd) { if(err) throw err //buf根據(jù)你的需要設(shè)定每次的讀取長度 var buf = new Buffer.alloc(225); //讀取fd文件內(nèi)容到buf緩存區(qū),如果position設(shè)置為null就會從最開始的地方開始讀 fs.read(fd, buf, 0, 20, null, function (err, bytesRead, buffer) { console.log(buf.slice(0, bytesRead).toString()); console.log(buf); }); });
fs.write寫文件,基于open
參數(shù)
- fd
<integer>
- buffer
<Buffer>
|<Uint8Array>
- offset
<integer>
- length
<integer>
- position
<integer>
- callback
<Function>
- err
<Error>
- bytesWritten
<integer>
- buffer
<Uint8Array>
- err
參數(shù)的值
寫入 buffer 到 fd 指定的文件。
offset 決定 buffer 中被寫入的部分,length 是一個整數(shù),指定要寫入的字節(jié)數(shù)。
position 指向從文件開始寫入數(shù)據(jù)的位置的偏移量。 如果 typeof position !== ‘number’,則數(shù)據(jù)從當(dāng)前位置寫入。詳見 pwrite(2)。
回調(diào)有三個參數(shù) (err, bytesWritten, buffer),其中 bytesWritten 指定從 buffer 寫入了多少字節(jié)。
如果以 util.promisify() 的形式調(diào)用該方法,則會返回包含 bytesWritten 和 buffer 屬性的 Promise 的對象。
注意,多次對同一文件使用 fs.write 且不等待回調(diào),是不安全的。 對于這種情況,強(qiáng)烈推薦使用 fs.createWriteStream。
在 Linux 上,當(dāng)文件以追加模式打開時,指定位置的寫入是不起作用的。 內(nèi)核會忽略位置參數(shù),并總是將數(shù)據(jù)追加到文件的末尾。
例子
var fs = require('fs'); fs.open('./1.txt', 'r+', function (err, fd) { if(err) throw err fs.write(fd, '文件追加字符測試', 20, 'utf-8', (err, written, buffer)=>{ console.log(err); console.log(buffer); }) });
寫入前,別眨眼:
寫入后的結(jié)果,寫入位置20,打印err,空,打印buffer,剛才寫入的字符串!
- 提升:位置和數(shù)據(jù)的寫入和讀取就是各位通過邏輯來控制的了!
文件流Stream
- 想象一下,如果把文件讀取比作向一個池子里抽水,同步會阻塞程序,異步會等待結(jié)果,如果這個池子特別大怎么辦?有三峽水庫那么大怎么辦?你要等到多久才能喝到抽的水?
- 因此便會有了文件流,文件流就好比你一邊抽一邊取,不用等池子滿了再用一樣方便。
- 因為流在文件讀寫里非常抽象,所以并不能明顯確定,只能勉強(qiáng)通過一些特征表示;
- fs繼承于Stream;
讀取流例子
const fs = require("fs"); console.log(111111); const read = fs.createReadStream('1.txt') read.setEncoding('utf-8') read.resume();//讓文件流開始'流'動起來 read.on('data',data =>{//監(jiān)聽讀取的數(shù)據(jù),如果打印data就是文件的內(nèi)容 console.log('正在讀'); }) read.on('end', () => { //監(jiān)聽狀態(tài) console.log('文件讀取結(jié)束'); }) console.log(222222);
我這里準(zhǔn)備了2萬+行的數(shù)據(jù),監(jiān)聽的data打印了多少次就說明被流監(jiān)聽了多少次,期間就可以使用多少次這個數(shù)據(jù),用于一些超大型的數(shù)據(jù)讀取還是非常有效的。
寫入流
const fs = require("fs"); console.log(111111); const read = fs.createReadStream('1.txt') read.setEncoding('utf-8') read.resume();//讓文件流開始'流'動起來 read.on('data',data =>{ console.log('正在讀'); }) read.on('end', () => { //監(jiān)聽狀態(tài) console.log('文件讀取結(jié)束'); }) console.log(222222); console.log(333333); const write = fs.createWriteStream('2.txt') read.pipe(write) //pipe就是那根水管,抽向2.txt console.log(444444);
如果你自己測試的時候看著2.txt的行數(shù)在飛漲,會更加直觀!
- Node.js中的流(Stream)的作用詳解
- Node.js數(shù)據(jù)流Stream之Duplex流和Transform流用法
- Node.js數(shù)據(jù)流Stream之Readable流和Writable流用法
- node.js中stream流中可讀流和可寫流的實現(xiàn)與使用方法實例分析
- node.js使用stream模塊實現(xiàn)自定義流示例
- Node.js中你不可不精的Stream(流)
- Node.js中流(stream)的使用方法示例
- Node.js中的流(Stream)介紹
- Node.js 中的流Stream模塊簡介及如何使用流進(jìn)行數(shù)據(jù)處理
相關(guān)文章
Node.js實現(xiàn)大文件斷點續(xù)傳示例詳解
這篇文章主要為大家介紹了Node.js實現(xiàn)大文件斷點續(xù)傳示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11實戰(zhàn)node靜態(tài)文件服務(wù)器的示例代碼
本篇文章主要介紹了實戰(zhàn)node靜態(tài)文件服務(wù)器的示例,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2018-03-03node.js中module.exports與exports用法上的區(qū)別
Node.js 引入了模塊(Module)概念,一個模塊可以通過module.exports 或 exports 將函數(shù)、變量等導(dǎo)出,以使其它 JavaScript 腳本通過require() 函數(shù)引入并使用。那么node.js中module.exports與exports有什么區(qū)別呢?下面小編給大家解答下2016-09-09為nuxt項目寫一個面包屑cli工具實現(xiàn)自動生成頁面與面包屑配置
這篇文章主要介紹了為nuxt項目寫一個面包屑cli工具實現(xiàn)自動生成頁面與面包屑配置,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09Nodejs-cluster模塊知識點總結(jié)及實例用法
在本篇文章里小編給大家整理的是一篇關(guān)于Nodejs-cluster模塊知識點總結(jié)及實例用法,有興趣的朋友們可以跟著學(xué)習(xí)下。2021-12-12Node.js中多進(jìn)程模塊Cluster的介紹與使用
眾所周知Node.js是單線程的,一個單獨的Node.js進(jìn)程無法充分利用多核。Node.js從v0.6.0開始,新增cluster模塊,讓Node.js開發(fā)Web服務(wù)時,很方便的做到充分利用多核機(jī)器。這篇文章主要給大家介紹了關(guān)于Node.js中多進(jìn)程模塊Cluster的相關(guān)資料,需要的朋友可以參考下2017-05-05使用Nodejs連接mongodb數(shù)據(jù)庫的實現(xiàn)代碼
這篇文章主要介紹了使用Nodejs連接mongodb數(shù)據(jù)庫的實現(xiàn)代碼,需要的朋友可以參考下2017-08-08