Node.js從字符串生成文件流的實(shí)現(xiàn)方法
一.背景
在文件相關(guān)的數(shù)據(jù)加工等場(chǎng)景下,經(jīng)常面臨生成的物理文件應(yīng)該如何處理的問(wèn)題,比如:
生成的文件放到哪里,路徑存在不存在?
臨時(shí)文件何時(shí)清理,如何解決命名沖突,防止覆蓋?
并發(fā)場(chǎng)景下的讀寫(xiě)順序如何保證?
……
對(duì)于讀寫(xiě)物理文件帶來(lái)的這些問(wèn)題,最好的解決辦法就是 不寫(xiě)文件 。然而,一些場(chǎng)景下想要不寫(xiě)文件可不那么容易,比如文件上傳
二.問(wèn)題
文件上傳一般通過(guò)表單提交來(lái)實(shí)現(xiàn),例如:
var FormData = require('form-data'); var fs = require('fs'); var form = new FormData(); form.append('my_file', fs.createReadStream('/foo/bar.jpg')); form.submit('example.org/upload', function(err, res) { console.log(res.statusCode); });
(摘自 Form-Data )
不想寫(xiě)物理文件的話(huà),可以這樣做:
const FormData = require('form-data'); const filename = 'my-file.txt'; const content = 'balalalalala...變身'; const formData = new FormData(); // 1.先將字符串轉(zhuǎn)換成Buffer const fileContent = Buffer.from(content); // 2.補(bǔ)上文件meta信息 formData.append('file', fileContent, { filename, contentType: 'text/plain', knownLength: fileContent.byteLength });
也就是說(shuō),文件流除了能夠提供數(shù)據(jù)外,還具有一些 meta 信息,如文件名、文件路徑等 ,而這些信息是普通 Stream 所不具備的。那么,有沒(méi)有辦法憑空創(chuàng)建一個(gè)“真正的”文件流?
三.思路
要想創(chuàng)建出“真正的”文件流,至少有正反 2 種思路:
給普通流添上文件相關(guān)的 meta 信息
先拿到一個(gè)真正的文件流,再改掉其數(shù)據(jù)和 meta 信息
顯然,前者更靈活一些,并且實(shí)現(xiàn)上能夠做到完全不依賴(lài)文件
文件流的生產(chǎn)過(guò)程
沿著憑空創(chuàng)造的思路,探究 fs.createReadStream API 的 內(nèi)部實(shí)現(xiàn) 之后發(fā)現(xiàn),生產(chǎn)文件流的關(guān)鍵過(guò)程如下:
function ReadStream(path, options) { // 1.打開(kāi)path指定的文件 if (typeof this.fd !== 'number') this.open(); } ReadStream.prototype.open = function() { fs.open(this.path, this.flags, this.mode, (er, fd) => { // 2.拿到文件描述符并持有 this.fd = fd; this.emit('open', fd); this.emit('ready'); // 3.開(kāi)始流式讀取數(shù)據(jù) // read來(lái)自父類(lèi)Readable,主要調(diào)用內(nèi)部方法_read // ref: https://github.com/nodejs/node/blob/v10.16.3/lib/_stream_readable.js#L390 this.read(); }); }; ReadStream.prototype._read = function(n) { // 4.從文件中讀取一個(gè)chunk fs.read(this.fd, pool, pool.used, toRead, this.pos, (er, bytesRead) => { let b = null; if (bytesRead > 0) { this.bytesRead += bytesRead; b = thisPool.slice(start, start + bytesRead); } // 5.(通過(guò)觸發(fā)data事件)吐出一個(gè)chunk,如果還有數(shù)據(jù),process.nextTick再次this.read,直至this.push(null)觸發(fā)'end'事件 // ref: https://github.com/nodejs/node/blob/v10.16.3/lib/_stream_readable.js#L207 this.push(b); }); };
P.S.其中第 5 步相對(duì)復(fù)雜, this.push(buffer) 既能觸發(fā)下一個(gè) chunk 的讀取( this.read() ),也能在數(shù)據(jù)讀完之后(通過(guò) this.push(null) )觸發(fā) 'end' 事件,具體見(jiàn) node/lib/_stream_readable.js
重新實(shí)現(xiàn)文件流
既然已經(jīng)摸清了文件流的生產(chǎn)過(guò)程,下一步自然是 替換掉所有文件操作,直至文件流的實(shí)現(xiàn)完全不依賴(lài)文件 ,例如:
// 從文件中讀取一個(gè)chunk fs.read(this.fd, pool, pool.used, toRead, this.pos, (er, bytesRead) => { /* ... */ }); // 換成 this._fakeReadFile(this.fd, pool, pool.used, toRead, this.pos, (bytesRead) => { /* ... */ }); // 從輸入字符串對(duì)應(yīng)的Buffer中copy出一個(gè)chunk ReadStream.prototype._fakeReadFile = function(_, buffer, offset, length, position, cb) { position = position || this.input._position; // fake read file async setTimeout(() => { let bytesRead = 0; if (position < this.input.byteLength) { bytesRead = this.input.copy(buffer, offset, position, position + length - 1); this.input._position += bytesRead; } cb(bytesRead); }, 0); }
即從中剔除文件操作,用基于字符串的操作去替代它們
四.解決方案
如此這般,就有了 ayqy/string-to-file-stream ,用來(lái)憑空創(chuàng)建文件流:
string2fileStream('string-content') === fs.createReadStream(/* path to a text file with content 'string-content' */)`
例如:
const string2fileStream = require('string-to-file-stream'); const input = 'Oh, my great data!'; const s = string2fileStream(input); s.on('data', (chunk) => { assert.equal(chunk.toString(), input); }); 生成的流同樣能夠具有文件 meta 信息: const string2fileStream = require('string-to-file-stream'); const formData = new FormData(); formData.append('file', string2fileStream('my-string-data', { path: './abc.txt' })); form.submit('example.org/upload', function(err, res) { console.log(res.statusCode); });
足夠以假亂真
參考資料
fs.createReadStream(path[, options])
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Node.js Streams文件讀寫(xiě)操作詳解
- node.js基于fs模塊對(duì)系統(tǒng)文件及目錄進(jìn)行讀寫(xiě)操作的方法詳解
- 基于node.js的fs核心模塊讀寫(xiě)文件操作(實(shí)例講解)
- Node.js實(shí)戰(zhàn)之Buffer和Stream模塊系統(tǒng)深入剖析詳解
- Node.js數(shù)據(jù)流Stream之Duplex流和Transform流用法
- Node.js數(shù)據(jù)流Stream之Readable流和Writable流用法
- node.js中stream流中可讀流和可寫(xiě)流的實(shí)現(xiàn)與使用方法實(shí)例分析
- node.js使用stream模塊實(shí)現(xiàn)自定義流示例
- 深入淺出了解Node.js Streams
- Node.js中你不可不精的Stream(流)
- Node.js fs模塊(文件模塊)創(chuàng)建、刪除目錄(文件)讀取寫(xiě)入文件流的方法
- node.js同步/異步文件讀寫(xiě)-fs,Stream文件流操作實(shí)例詳解
相關(guān)文章
Node.js實(shí)現(xiàn)登錄注冊(cè)功能
這篇文章主要為大家詳細(xì)介紹了Node.js實(shí)現(xiàn)登錄注冊(cè)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04nodejs之get/post請(qǐng)求的幾種方式小結(jié)
下面小編就為大家?guī)?lái)一篇nodejs之get/post請(qǐng)求的幾種方式小結(jié)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-07-07node強(qiáng)緩存和協(xié)商緩存實(shí)戰(zhàn)示例
這篇文章主要為大家介紹了node強(qiáng)緩存和協(xié)商緩存實(shí)戰(zhàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07詳解Nodejs中自動(dòng)化瀏覽器操作神器Puppeteer的使用
Puppeteer是一個(gè)JavaScript庫(kù),它提供了一種方式來(lái)通過(guò)DevTools協(xié)議控制無(wú)頭瀏覽器,本文主要為大家介紹了Puppeteer的主要特性和使用方法,感興趣的可以了解下2024-01-01DevEco?Studio設(shè)置Nodejs提示路徑只能包含英文、數(shù)字、下劃線(xiàn)等解決辦法
這篇文章主要給大家介紹了關(guān)于DevEco?Studio設(shè)置Nodejs提示路徑只能包含英文、數(shù)字、下劃線(xiàn)等的解決辦法,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2024-01-01Express URL跳轉(zhuǎn)(重定向)的實(shí)現(xiàn)方法
Express是一個(gè)基于Node.js實(shí)現(xiàn)的Web框架,其響應(yīng)HTTP請(qǐng)求的response對(duì)象中有兩個(gè)用于URL跳轉(zhuǎn)方法res.location()和res.redirect(),使用它們可以實(shí)現(xiàn)URL的301或302重定向。2017-04-04基于node.js的fs核心模塊讀寫(xiě)文件操作(實(shí)例講解)
下面小編就為大家?guī)?lái)一篇基于node.js的fs核心模塊讀寫(xiě)文件操作(實(shí)例講解)。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就想給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-09-09node.js解決客戶(hù)端請(qǐng)求數(shù)據(jù)里面中文亂碼的事件方法
本文主要介紹了node.js解決客戶(hù)端請(qǐng)求數(shù)據(jù)里面中文亂碼的事件方法,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-12-12Mongoose中document與object的區(qū)別示例詳解
這篇文章主要給大家介紹了關(guān)于Mongoose中document與object區(qū)別的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考借鑒,下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09