125*_*748 9 javascript filestream node.js
在示例代码这篇文章,怎么会是上线工作流的最后一段:
fs.createReadStream(filePath).pipe(brotli()).pipe(res)
Run Code Online (Sandbox Code Playgroud)
我理解第一部分是读取文件,第二部分是压缩它,但是什么是.pipe(res)?这似乎做我通常做的工作res.send或res.sendFile.
完整代码†:
const accepts = require('accepts')
const brotli = require('iltorb').compressStream
function onRequest (req, res) {
res.setHeader('Content-Type', 'text/html')
const fileName = req.params.fileName
const filePath = path.resolve(__dirname, 'files', fileName)
const encodings = new Set(accepts(req).encodings())
if (encodings.has('br')) {
res.setHeader('Content-Encoding', 'br')
fs.createReadStream(filePath).pipe(brotli()).pipe(res)
}
}
const app = express()
app.use('/files/:fileName', onRequest)
Run Code Online (Sandbox Code Playgroud)
localhost:5000/files/test.txt => Browser displays text contents of that file
†我稍微改变了使用快递,以及其他一些小的东西.
“简单地将数据传递到响应对象如何将数据呈现回客户端?”
问题中“响应对象”的措词可能意味着询问者正在试图理解为什么将数据从流中传送到res执行任何操作。误解是那res只是一些对象。
这是因为所有表达的Responses(res)都继承自http.ServerResponse(在此行),这是可写的Stream。因此,每当将数据写入时res,都会处理写入的数据,通过http.ServerResponse该操作内部将写入的数据发送回客户端。
在内部,res.send实际上只是写入它所代表的基础流(本身)。res.sendFile实际上将从文件读取的数据通过管道传输到自身。
如果不清楚将数据从一个流“管道”到另一个流的行为,请参阅底部的部分。
相反,如果发件人不清楚从文件到客户端的数据流,那么这里有一个单独的解释。
我想说,理解这一行的第一步是将其分解为更小,更易理解的片段:
首先,fs.createReadStream用于获取文件内容的可读流。
const fileStream = fs.createReadStream(filePath);
Run Code Online (Sandbox Code Playgroud)
接着,变换流的是将数据转换成压缩的格式被创建,并在数据fileStream被“管道”(通过)到它。
const compressionStream = brotli();
fileStream.pipe(compressionStream);
Run Code Online (Sandbox Code Playgroud)
最后,通过compressionStream(转换流)的数据将通过管道传递到响应中,该响应也是可写流。
compressionStream.pipe(res);
Run Code Online (Sandbox Code Playgroud)
当以视觉方式进行布局时,该过程非常简单:
现在,遵循数据流非常简单:数据首先来自文件,通过压缩器,最后到达响应,响应从内部将数据发送回客户端。
等待,但是压缩流如何传递到响应流中?
答案是pipe返回目标流。这意味着当您这样做时a.pipe(b),您将从b方法调用中返回。
以这条线a.pipe(b).pipe(c)为例。首先,a.pipe(b)进行评估,返回b。然后,.pipe(c)对的结果调用a.pipe(b),即b,因此等于b.pipe(c)。
a.pipe(b).pipe(c);
// is the same as
a.pipe(b); // returns `b`
b.pipe(c);
// is the same as
(a.pipe(b)).pipe(c);
Run Code Online (Sandbox Code Playgroud)
问题中的措词“暗示将数据传送到响应对象”也可能意味着询问者不理解数据流,认为数据直接从a到c。相反,上述内容应阐明数据从a到b,然后b到c;fileStream到compressionStream,然后compressionStream到res。
如果整个过程仍然没有意义,那么在没有流概念的情况下重写该过程可能会有所帮助:
首先,从文件中读取数据。
const fileContents = fs.readFileSync(filePath);
Run Code Online (Sandbox Code Playgroud)
然后fileContents将其压缩。使用某些compress功能即可完成。
function compress(data) {
// ...
}
const compressedData = compress(fileContents);
Run Code Online (Sandbox Code Playgroud)
最后,数据通过response发送回客户端res。
res.send(compressedData);
Run Code Online (Sandbox Code Playgroud)
问题中的原始代码行与上述过程大致相同,除非原始流中包含流。
从外部源(fs.readFileSync)接收一些数据的行为就像是可读的Stream。compress通过函数转换数据()的行为就像转换Stream。将数据发送到外部源(res.send)的行为就像是可写的Stream。
如果您对溪流的工作方式感到困惑,可以简单地类比一下:每种类型的溪流都可以在水(数据)从山顶湖水从山下流下来的情况下想到。
写的所有数据的一种便捷方式直接可读流的可写流中读取是只是pipe它,这是湖泊只有直接连接到人。
readable.pipe(writable); // easy & simple
Run Code Online (Sandbox Code Playgroud)
这与从可读流中读取数据,然后将其手动写入可写流形成对比:
// "pipe" data from a `readable` stream to a `writable` one.
readable.on('data', (chunk) => {
writable.write(chunk);
});
readable.on('end', () => writable.end());
Run Code Online (Sandbox Code Playgroud)
您可能会立即质疑为什么Transform流与Duplex流相同。两者之间的唯一区别是它们的实现方式。
转换流实现了_transform应该接收写入数据并返回可读数据的功能,而双工流只是简单的Readable和Writable流,因此必须实现_read和_write。
我不确定我是否正确理解你的问题。但我将尝试解释代码fs.createReadStream(filePath).pipe(brotli()).pipe(res),希望能够澄清您的疑问。
如果你检查 的源代码iltorb,compressStream会返回一个TransformStreamEncode扩展的对象Transform。正如您所看到的,转换流实现了 Readable 和 Writable 接口。因此,当fs.createReadStream(filePath).pipe(brotli())执行时,TransformStreamEncode的可写接口用于写入从 中读取的数据filePath。.pipe(res)现在,当执行下一次调用时,TransformStreamEncode将使用 的可读接口来读取压缩数据并将其传递给res. 如果您检查HTTP Response对象的文档,它实现了 Writable 接口。因此它内部处理pipe从 Readable 读取压缩数据的事件TransformStreamEncode,然后将其发送到客户端。
HTH。
| 归档时间: |
|
| 查看次数: |
1004 次 |
| 最近记录: |