Javascript 如何简单地通过管道将响应对象的数据呈现给客户端?
在中的示例代码中,流的最后一段是如何在线路上工作的:Javascript 如何简单地通过管道将响应对象的数据呈现给客户端?,javascript,node.js,filestream,Javascript,Node.js,Filestream,在中的示例代码中,流的最后一段是如何在线路上工作的: fs.createReadStream(filePath).pipe(brotli()).pipe(res) 我知道第一部分读取文件,第二部分压缩文件,但是什么是.pipe(res)?它似乎完成了我通常使用res.send或res.sendFile所做的工作 完整代码†: localhost:5000/files/test.txt=>浏览器显示该文件的文本内容 简单地将数据管道化到响应对象如何将数据返回到客户端? †我稍微改了一下,用了ex
fs.createReadStream(filePath).pipe(brotli()).pipe(res)
我知道第一部分读取文件,第二部分压缩文件,但是什么是.pipe(res)
?它似乎完成了我通常使用res.send
或res.sendFile
所做的工作
完整代码†:
localhost:5000/files/test.txt=>浏览器显示该文件的文本内容
简单地将数据管道化到响应对象如何将数据返回到客户端?
†我稍微改了一下,用了express和其他一些小东西。我不确定我是否正确理解了你的问题。但我将尝试解释代码
fs.createReadStream(filePath).pipe(brotli()).pipe(res)
,希望这能澄清您的疑问
如果检查的源代码,compressStream
返回扩展的transformstreamncode
对象。正如您所看到的,转换流实现了可读和可写接口。因此,当执行fs.createReadStream(filePath).pipe(brotli())
时,TransformStreamEncode
的可写接口用于写入从filePath
读取的数据。现在,当执行对.pipe(res)
的下一次调用时,将使用transformstreamncode
的可读接口读取压缩数据,并将其传递给res
。如果您检查对象的文档,它将实现可写接口。因此,它在内部处理pipe
事件,从ReadableTransformStreamEncode
读取压缩数据,然后将其发送到客户端
嗯。你问:
简单地将数据管道化到响应对象如何将数据返回到客户端
大多数人将“渲染X”理解为“生成X的某些视觉表示”。在浏览器上渲染从文件系统读取的文件之前,将数据发送到浏览器(此处,通过管道)是一个必要的步骤,但管道不是渲染的目的。发生的情况是,Express应用程序获取文件内容,对其进行压缩,并将压缩后的流原样发送到浏览器。这是一个必要的步骤,因为如果浏览器没有数据,则无法呈现任何内容。因此,.pipe
仅用于将数据传递给发送到浏览器的响应
它本身不会“渲染”,也不会告诉浏览器如何处理数据。在管道之前,会发生以下情况:res.setHeader('Content-Type','text/html')
。因此,浏览器将看到一个标题,告诉它内容是HTML。浏览器知道如何处理HTML:显示它。因此,它将获取获取的数据,对其进行解压缩(因为内容编码
头告诉它已压缩),将其解释为HTML,并将其显示给用户,即呈现它
什么是.pipe(res)
?它似乎完成了我通常使用res.send
或res.sendFile
所做的工作
.pipe
用于将可读流的全部内容传递给可写流。在处理流时,这是一种方便的方法。当必须从流中读取以获取要包含在响应中的数据时,使用.pipe
发送响应是有意义的。如果不必从流中读取,则应使用.send
或.sendFile
。它们可以执行很好的簿记任务,比如设置内容长度
标题,否则您必须自己做
事实上,您展示的示例在执行内容协商方面做得很差。应该重写该代码,以使用
res.sendFile
将文件发送到浏览器,压缩处理应该由设计用于内容协商的中间件完成,因为它不仅仅支持br
方案。阅读此文以获得答案:
我将引用Interrestant部分:
a.pipe(b).pipe(c).pipe(d)
# Which is equivalent to:
a.pipe(b)
b.pipe(c)
c.pipe(d)
# Which, in Linux, is equivalent to:
$ a | b | c | d
因此fs.createReadStream(filePath).pipe(brotli()).pipe(res)
相当于var readableStream=fs.createReadStream(filePath).pipe(brotli());readableStream.pipe(res)
及
因此Node.js读取文件并将其转换为可读流对象fs.createReadStream(filePath)
。
然后它将内容传递给iltorb库,该库创建另一个可读流.pipe(brotli())
(包含压缩内容),最后将内容传递给可写流res
。因此nodejs在内部调用res.write()
,将其写回浏览器
“简单地将数据管道化到响应对象如何将数据返回到客户端?”
问题中“响应对象”的措辞可能意味着提问者试图理解从流到res
的管道数据做任何事情的原因。误解是,res
只是一个对象
这是因为所有(res
)都继承自(),,这是一个可写的流。因此,无论何时将数据写入res
,写入的数据都由http.ServerResponse
处理,后者在内部将写入的数据发送回客户端
在内部,res.send
实际上只是写入它所表示的底层流(自身)。从文件读取到文件本身
如果不清楚从一个流到另一个流的“管道”数据行为,请参阅底部的部分
相反,如果询问者不清楚从文件到客户端的数据流,那么这里有一个单独的解释
我想说,理解这条线的第一步是把它分解成更小、更容易理解的片段:
首先,fs.createReadStream
用于获取文件内容的详细信息
const fileStream = fs.createReadStream(filePath);
接下来是一个将数据转换为com的
# readable.pipe(writable)
readable.on('data', (chunk) => {
writable.write(chunk);
});
readable.on('end', () => {
writable.end();
});
const fileStream = fs.createReadStream(filePath);
const compressionStream = brotli();
fileStream.pipe(compressionStream);
compressionStream.pipe(res);
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);
const fileContents = fs.readFileSync(filePath);
function compress(data) {
// ...
}
const compressedData = compress(fileContents);
res.send(compressedData);
readable.pipe(writable); // easy & simple
// "pipe" data from a `readable` stream to a `writable` one.
readable.on('data', (chunk) => {
writable.write(chunk);
});
readable.on('end', () => writable.end());