【问题标题】:How do I write a Node.js module to handle an incoming piped stream如何编写 Node.js 模块来处理传入的管道流
【发布时间】:2016-02-04 22:59:05
【问题描述】:

我正在尝试编写一个接受传入管道二进制(或 base-64 编码)流的节点模块,但坦率地说,我什至不知道从哪里开始。我在 Node 文档中看不到任何关于 handling 传入流的示例;我只看到使用它们的示例?

比如说我希望能够做到这一点:

var asset = new ProjectAsset('myFile', __dirname + '/image.jpg')
var stream = fs.createReadStream(__dirname + '/image.jpg', { encoding: 'base64' }).pipe(asset)
stream.on('finish', function() {
    done()
})

ProjectAsset 看起来像这样,但我不知道下一步该去哪里:

'use strict'

var stream = require('stream'),
    util = require('util')

var ProjectAsset = function() {
    var self = this

    Object.defineProperty(self, 'binaryData', {
        configurable: true,
        writable: true
    })

    stream.Stream.call(self)

    self.on('pipe', function(src) {
        // does it happen here? how do I set self.binaryData?
    })

    return self
}

util.inherits(ProjectAsset, stream.Stream)

module.exports = ProjectAsset
module.exports.DEFAULT_FILE_NAME = 'file'

【问题讨论】:

  • 这可能有助于提及缓冲区模块可以处理二进制数据,因此如果您在同一模块中读取和翻译二进制文件,您可能需要查看缓冲区模块
  • 我实际上是在手动设置时使用 Buffer 来存储数据 - 我需要将管道流转换为缓冲区,以便确定内容类型和准确的长度。
  • 因此,如果您知道如何使用流 api,其中一个选项是“数据”上的偶数侦听器,如果您只是将该数据附加到缓冲区,它应该会得到您要查找的内容
  • 想添加答案吗?
  • 是的,我将其添加到答案中

标签: javascript node.js stream


【解决方案1】:

可以从stream.Stream 继承并使其工作,但是根据documentation 中可用的内容,我建议从stream.Writable 继承。通过管道连接到 stream.Writable,您需要定义 _write(chunk, encoding, done) 来处理管道。这是一个例子:

var asset = new ProjectAsset('myFile', __dirname + '/image.jpg')
var stream = fs.createReadStream(__dirname + '/image.jpg', { encoding: 'base64' }).pipe(asset)
stream.on('finish', function() {
    console.log(asset.binaryData);
})

项目资产

'use strict'

var stream = require('stream'),
    util = require('util')

var ProjectAsset = function() {
    var self = this

    self.data
    self.binaryData = [];

    stream.Writable.call(self)

    self._write = function(chunk, encoding, done) {
        // Can handle this data however you want
        self.binaryData.push(chunk.toString())
        // Call after processing data
        done()
    }
    self.on('finish', function() {
        self.data = Buffer.concat(self.binaryData)
    })

    return self
}

util.inherits(ProjectAsset, stream.Writable)

module.exports = ProjectAsset
module.exports.DEFAULT_FILE_NAME = 'file'

如果您还想从stream 中读取数据,请查看从stream.Duplex 继承并包括_read(size) 方法。

还有simplified constructors api,如果你做的事情更简单的话。

【讨论】:

  • 抱歉,花了这么长时间,但到目前为止,这看起来像是要走的路。只有最后一点是 binaryData 仍然是一个字节数组;不知何故,我还需要将其转换为缓冲区,以便我可以从中推断内容类型等。
  • Hrm,当我尝试fs.writeFile(__dirname + '/image2.jpg', asset.binaryData)时,输出文件与输入不同。事实上,它甚至不能渲染为 jpg。
  • 为此,您需要查看节点中的 Buffer 类。我并没有搞砸太多,但是您可以尝试从将缓冲区初始化为self.binaryData = new Buffer(''); 开始,从这里您需要在_write 函数中执行类似这样的操作将数据添加到缓冲区中:self.binaryData = Buffer.concat([self.binaryData, new Buffer(chunk, encoding)])。您可能需要使用编码并尝试在 writeFile 调用上显式设置编码。
  • 所以这里缺少的部分是我需要在二进制数据数组上调用 Buffer.concat()。我现在必须弄清楚如何处理赏金,因为您和@Binvention 都提供了同等的帮助!
  • 刚刚在对我有用的最后一篇文章中添加到您的答案中。
【解决方案2】:

我不确定这是否正是您要查找的内容,但我认为您可以在缓冲区数组上使用带有Buffer.concat 的缓冲区api 处理它,这些缓冲区可以从chunk 在流data 侦听器上检索

'use strict'

var stream = require('stream'),
    util = require('util');

var ProjectAsset = function() {
    var self = this

    Object.defineProperty(self, 'binaryData', {
        configurable: true,
        writable: true
    })

    stream.Stream.call(self)
    var data;
    var dataBuffer=[];
    self.on('data', function(chunk) {
        dataBuffer.push(chunk);
    }).on('end',function(){
        data=Buffer.concat(dataBuffer);
    });
    self.binaryData=data.toString('binary');
    return self
}

util.inherits(ProjectAsset, stream.Stream)

module.exports = ProjectAsset
module.exports.DEFAULT_FILE_NAME = 'file'

【讨论】:

  • 不错!不是一个可行的例子,但最后,我采用了@pohlman 的代码,最后在传入的缓冲区数组上简单地使用了 Buffer.concat。
  • 是的,很抱歉我没有时间测试代码,但很高兴我能提供帮助
【解决方案3】:

由于您使用var asset = new ProjectAsset('myFile', __dirname + '/image.jpg'),我想您的 ProjectAsset 职责是对一些输入流进行一些转换并将其写入文件。您可以实现转换流,因为您从流中接收一些输入并生成一些输出,这些输出可以保存到文件或其他写入流中。

您当然可以通过从 node.js 继承 Transform Stream 来实现转换流,但是继承非常麻烦,所以我的实现使用 through2 来实现转换流:

module.exports = through2(function (chunk, enc, callback) {
  // This function is called whenever a piece of data from the incoming stream is read
  // Transform the chunk or buffer the chunk in case you need more data to transform

  // Emit a data package to the next stream in the pipe or omit this call if you need more data from the input stream to be read
  this.push(chunk);

  // Signal through2 that you processed the incoming data package
  callback();
 }))

用法

var stream = fs.createReadStream(__dirname + '/image.jpg', { encoding: 'base64' })
               .pipe(projectAsset)
               .pipe(fs.createWriteStream(__dirname + '/image.jpg'));

正如您在此示例中所见,实现流管道将数据转换和数据保存完全解耦。

工厂函数

如果您喜欢在项目资产模块中使用类似构造函数的方法,因为您需要传递一些值或东西,您可以轻松导出构造函数,如下所示

var through2 = require('through2');

module.exports = function(someData) {

  // New stream is returned that can use someData argument for doing things
  return through2(function (chunk, enc, callback) {
    // This function is called whenever a piece of data from the incoming stream is read
    // Transform the chunk or buffer the chunk in case you need more data to transform

    // Emit a data package to the next stream in the pipe or omit this call if you need more data from the input stream to be read
    this.push(chunk);

    // Signal through2 that you processed the incoming data package
    callback();
  });
}

用法

var stream = fs.createReadStream(__dirname + '/image.jpg', { encoding: 'base64' })
               .pipe(projectAsset({ foo: 'bar' }))
               .pipe(fs.createWriteStream(__dirname + '/image.jpg'));

【讨论】:

  • 我更多地将其视为代理 - 从 fs 加载的可能性较小,而从请求加载的可能性更大。我使用 fs 只是为了构造函数和编写测试。
  • 我喜欢这个想法,但为了尽量减少第 3 方的依赖,我将在本地实现它。感谢through2 的提示!
  • @remus through2 更好,不太可能把事情搞砸。而且更容易。虽然是 YMMW。
  • 好吧,首先,我不能在 through2 中包装我的类 - 我在 module.exports 中返回类而不是它的实例,我可以这样做var asset = new ProjectAsset
  • 您可以从 module.exports 返回工厂函数,而不是封装实例创建方式的构造函数。我的提示:不要在 JavaScript 中进行太多 OOP :)
猜你喜欢
  • 2020-03-05
  • 1970-01-01
  • 2014-09-13
  • 2017-07-07
  • 1970-01-01
  • 2012-10-03
  • 1970-01-01
  • 2017-02-09
  • 1970-01-01
相关资源
最近更新 更多