pipe(Node.js 的流)和 bl(BufferList)如何协同工作?

Posted

技术标签:

【中文标题】pipe(Node.js 的流)和 bl(BufferList)如何协同工作?【英文标题】:How do pipe (stream of Node.js) and bl (BufferList) work together? 【发布时间】:2015-09-04 04:14:02 【问题描述】:

这实际上是 Node.js 教程中的第 8 题 ([https://github.com/workshopper/learnyounode][1])

目标: 编写一个程序,对作为第一个命令行参数提供给您的 URL 执行 HTTP GET 请求。从服务器收集所有数据(不仅仅是第一个“数据”事件),然后将两行写入控制台(stdout)。

您编写的第一行应该只是一个整数,表示从服务器接收到的字符数。第二行应包含服务器发送的完整字符串。

所以这是我的解决方案(它通过了,但与官方解决方案相比看起来更丑)。

var http = require('http'),
bl = require('bl');

var myBL = new bl(function(err, myBL)
    console.log(myBL.length);
    console.log(myBL.toString());
);

var url = process.argv[2];
http.get(url, function(res)
    res.pipe(myBL);
    res.on('end', function()
        myBL.end();
    );
);

官方解决方案:

var http = require('http')
var bl = require('bl')

http.get(process.argv[2], function (response) 
    response.pipe(bl(function (err, data) 
        if (err)
            return console.error(err)
        data = data.toString()
        console.log(data.length)
        console.log(data)
    ))
)

我很难理解官方解决方案的工作原理。我主要有两个问题:

    bl 构造函数期望第二个参数是 bl (根据 bl 模块的文档, [https://github.com/rvagg/bl#new-bufferlist-callback--buffer--buffer-array-][2]), 但什么是数据?它不知从何而来。它应该是未定义的 当它被传递来构造 bl 实例时。

    什么时候是bl.end() 叫?我看不到bl.end() 的名字在哪里......

希望有人能对这些问题有所了解。 (我知道我应该阅读源代码,但你知道......)

  [1]: https://github.com/workshopper/learnyounode
  [2]: https://github.com/rvagg/bl#new-bufferlist-callback--buffer--buffer-array-

【问题讨论】:

【参考方案1】:

bl github 页面的这一部分或多或少地回答了您的问题:

在构造函数中给它一个回调并像使用它一样使用它 连接流:

const bl = require('bl')
, fs = require('fs')

fs.createReadStream('README.md')   
   .pipe(bl(function (err, data)  //  note 'new' isn't strictly required
      // `data` is a complete Buffer object containing the full data
      console.log(data.toString())   
))

请注意,当您像这样使用回调方法时,结果 data 参数是所有 Buffer 对象的串联 列表。如果你想避免这种连接的开销(在 极端性能意识的情况下),然后避免回调 方法,然后只听“结束”,就像标准的流一样。

您将 callback 传递给 bl,这基本上是一个函数,当它有数据流要做某事时会调用它。因此,数据现在是未定义的......它只是一个参数名称,稍后将用于传递来自 GET 调用的文本以进行打印。

我相信 bl.end() 没有被调用,因为让它运行没有真正的性能开销,但我可能是错的。

【讨论】:

谢谢两位!确实,我的错,我应该更仔细地阅读文档。 1) data 参数用作输出参数,bl 不要求它是 bl 的实例。 (我被这里的描述误导了github.com/rvagg/… 2) 对于 end() 调用,确实应该由 stream.pipe() nodejs.org/api/… 调用【参考方案2】:

我已经阅读了bl库和node stream API的源代码。

BufferList 是一个自定义的双工流,既可读又可写。当您运行readableStream.pipe(BufferList) 时,默认情况下end() 在源流发出end() 时在BufferList 上作为目标调用将没有更多数据可供读取。

查看implementation 的BufferList.prorotype.end

BufferList.prototype.end = function (chunk) 
  DuplexStream.prototype.end.call(this, chunk)

  if (this._callback) 
    this._callback(null, this.slice())
    this._callback = null
  

所以传递给BufferList的回调,将在BufferList从源流中接收到所有数据后调用,调用this.slice()将返回连接BufferList中所有Buffer的结果,其中@987654334 @参数来自。

【讨论】:

【参考方案3】:
var request=require('request')
request(process.argv[2],function(err,response,body)
console.log(body.length);
console.log(body);
)

你可以看看这个方法来解决上面的练习, ps request 虽然是第三方模块

【讨论】:

以上是关于pipe(Node.js 的流)和 bl(BufferList)如何协同工作?的主要内容,如果未能解决你的问题,请参考以下文章

Node.js 请求下载文件有时为空或不存在

不写入创建的流可写 node.js

Node.js 中的面向行的流

如何在 node.js 中搜索字符串的流?

Node.js中使用pipe拷贝大文件不能完全拷贝的解决办法

流模块基础