简单管道到响应对象如何将数据呈现给客户端?

125*_*748 9 javascript filestream node.js

在示例代码这篇文章,怎么会是上线工作流的最后一段:

fs.createReadStream(filePath).pipe(brotli()).pipe(res)
Run Code Online (Sandbox Code Playgroud)

我理解第一部分是读取文件,第二部分是压缩它,但是什么是.pipe(res)?这似乎做我通常做的工作res.sendres.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

简单地将数据传递给响应对象如何将数据呈现给客户端?

†我稍微改变了使用快递,以及其他一些小的东西.

Cla*_*vin 9

“简单地将数据传递到响应对象如何将数据呈现回客户端?”

问题中“响应对象”的措词可能意味着询问者正在试图理解为什么将数据从流中传送到res执行任何操作。误解是那res只是一些对象。

这是因为所有表达的Responsesres)都继承自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)

<code>管道</ code>流程图

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)

问题中的措词“暗示将数据传送到响应对象”也可能意味着询问者不理解数据流,认为数据直接从ac。相反,上述内容应阐明数据从ab,然后bcfileStreamcompressionStream,然后compressionStreamres


代码类比

如果整个过程仍然没有意义,那么在没有流概念的情况下重写该过程可能会有所帮助:

首先,从文件中读取数据。

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)接收一些数据的行为就像是可读的Streamcompress通过函数转换数据()的行为就像转换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


Viv*_*lye 0

我不确定我是否正确理解你的问题。但我将尝试解释代码fs.createReadStream(filePath).pipe(brotli()).pipe(res),希望能够澄清您的疑问。

如果你检查 的源代码iltorbcompressStream会返回一个TransformStreamEncode扩展的对象Transform。正如您所看到的,转换流实现了 Readable 和 Writable 接口。因此,当fs.createReadStream(filePath).pipe(brotli())执行时,TransformStreamEncode的可写接口用于写入从 中读取的数据filePath.pipe(res)现在,当执行下一次调用时,TransformStreamEncode将使用 的可读接口来读取压缩数据并将其传递给res. 如果您检查HTTP Response对象的文档,它实现了 Writable 接口。因此它内部处理pipe从 Readable 读取压缩数据的事件TransformStreamEncode,然后将其发送到客户端。

HTH。