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
事件,从Readable
TransformStreamEncode
读取压缩数据,然后将其发送到客户端

嗯。

你问:

简单地将数据管道化到响应对象如何将数据返回到客户端

大多数人将“渲染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());