天道酬勤,学无止境

Node.js流如何工作?(How do Node.js Streams work?)

问题

我有一个关于Node.js流的问题-特别是它们在概念上如何工作。

不缺少有关如何使用流的文档。 但是我很难找到流在数据级别如何工作。

我对Web通信HTTP的有限理解是,来回发送完整的数据“包”。 类似于个人订购公司目录,客户端向服务器发送GET(目录)请求,然后服务器以目录进行响应。 浏览器不会收到目录的页面,而是会收到整本书。

节点流可能是多部分消息吗?

我喜欢REST模型-尤其是它是无状态的。 浏览器和服务器之间的每一次交互都是完全自给自足的。 因此,节点流不是RESTful的吗? 一位开发人员提到与插座管的相似之处,插座使连接保持打开状态。 回到我的目录订购示例,这就像是一条信息电视广告,其行为“但是,等等!还有更多!” 而不是完全包含的目录?

流的很大一部分是接收器“下游”能够在上游发送“暂停”和“继续”之类的消息的能力。 这些消息由什么组成? 他们是POST吗?

最后,我对Node工作原理的有限视觉理解包括此事件循环。 可以将函数放置在与线程池分离的线程上,然后事件循环继续进行。 但是,在数据流完成之前,是否不应该发送数据流使事件循环占据(即停止)? 还如何监视下游的“暂停”请求?n事件循环是否将流放在池中的另一个线程上,并且当遇到“暂停”请求时,检索相关线程并暂停它?

我已经阅读了node.js文档,完成了nodeschool教程,构建了一个heroku应用,购买了两本书(真实,自含,书籍,有点像之前讲过的目录,可能不喜欢节点流),问了几个“节点”代码训练营的讲师-所有人都在谈论如何使用流,但没有人谈论下面实际发生的事情。

也许您遇到了很好的资源,解释了它们是如何工作的? 对于非CS头脑来说,也许是一个很好的拟人类比?

回答1

首先要注意的是:node.js流不仅限于HTTP请求。 HTTP请求/网络资源只是node.js中流的一个示例。

对于所有可以小块处理的事物,流都是有用的。 它们使您能够以较小的块处理潜在的巨大资源,从而更轻松地将其装入RAM。

假设您有一个文件(大小为数GB),并且想要将所有小写字母转换为大写字母并将结果写入另一个文件。 天真的方法将使用fs.readFile读取整个文件(为简便起见,省略了错误处理):

fs.readFile('my_huge_file', function (err, data) {
    var convertedData = data.toString().toUpperCase();

    fs.writeFile('my_converted_file', convertedData);
});

不幸的是,此方法很容易使您的RAM不堪重负,因为在处理文件之前必须先存储整个文件。 您还将浪费宝贵的时间等待文件被读取。 以较小的块处理文件是否有意义? 您可以在等待硬盘提供剩余数据的同时获得第一个字节后立即开始处理:

var readStream = fs.createReadStream('my_huge_file');
var writeStream = fs.createWriteStream('my_converted_file');
readStream.on('data', function (chunk) {
    var convertedChunk = chunk.toString().toUpperCase();
    writeStream.write(convertedChunk);
});
readStream.on('end', function () {
    writeStream.end();
});

这种方法更好:

  1. 您将只处理很容易装入RAM的一小部分数据。
  2. 一旦第一个字节到达,您就开始处理,不要浪费时间做任何事情,而要等待。

打开流后,node.js将打开文件并开始从中读取文件。 一旦操作系统将一些字节传递给正在读取文件的线程,它将被传递给您的应用程序。


回到HTTP流:

  1. 第一个问题在这里也是有效的。 攻击者可能会向您发送大量数据,以淹没您的RAM并关闭(DoS)您的服务。
  2. 但是,在这种情况下,第二个问题更为重要:网络可能非常慢(例如智能手机),并且可能需要很长时间才能将所有内容发送到客户端。 通过使用流,您可以开始处理请求并缩短响应时间。

暂停HTTP流时:这不是在HTTP级别完成的,但是要低得多。 如果您暂停流,node.js将仅停止从底层TCP套接字读取。 然后,所发生的事情取决于内核。 它仍然可以缓冲传入的数据,因此在您完成当前工作后就可以使用了。 它还可能在TCP级别通知发送方它应该暂停发送数据。 应用程序无需对此进行处理。 那不关他们的事。 实际上,发件人应用程序甚至可能没有意识到您不再积极阅读!

因此,基本上,这是关于在可用时立即提供数据,但又不会占用您的资源。 底层的工作由操作系统(例如netfshttp )或您使用的流的作者(例如zlib ,它是一种Transform流,通常通过螺栓连接到fsnet )完成。

回答2

对于节点流类,以下图表似乎是一个非常准确的10.000英尺概览/图表。

它表示由Chris Dickinson贡献的streams3

回答3

那么,首先,什么是流? 好吧,借助流,我们可以逐段处理含义的读和写数据,而无需完成整个读或写操作。 因此,我们不必将所有数据保留在内存中即可执行这些操作。

例如,当我们使用流读取文件时,我们读取部分数据,对其进行处理,然后释放内存,然后重复此操作,直到处理完整个文件为止。 或者想想YouTube和Netflix,它们之所以被称为流媒体公司是因为它们使用相同的原理来流媒体视频。

因此,不必等到整个视频文件加载完毕,而是逐段或逐块地进行处理,这样,即使在下载整个文件之前,您也可以开始观看。 因此,这里的原理不仅仅涉及Node.JS。 但是一般来说对于计算机科学是普遍的。

正如您所看到的,这使流成为处理大量数据(例如视频或我们正在从外部来源逐段接收的数据)的理想选择。 同样,流传输使数据处理在内存方面更加有效,因为不需要将所有数据都保留在内存中,也就时间而言,因为我们可以在数据到达时开始处理数据而不是等到所有到达时才开始处理数据

如何在Node.JS中实现它们:

因此,在Node中,有四种基本类型的流:可读流,可写流,双工流和转换流。 但是可读性和可写性是最重要的,可读性流是我们可以读取并消费数据的流。 流在核心Node模块中无处不在,例如,http服务器收到请求时进入的数据实际上是可读流。 因此,与请求一起发送的所有数据都是逐块而不是大块的。 同样,来自文件系统的另一个示例是,我们可以使用FS模块中的读取屏幕逐个读取文件,这实际上对于大型文本文件非常有用。

好了,要注意的另一件事是流实际上是EventEmitter类的实例。 这意味着所有流都可以发出并侦听命名的事件。 对于可读流,它们可以发出,并且我们可以侦听许多不同的事件。 但是最重​​要的两个是数据结束事件当有新数据要消耗时发出data事件,而在没有更多数据要消耗时将发出end事件。 当然,我们可以对这些事件做出相应的反应。

最后,除了事件,我们还有重要的功能可以在流中使用。 对于可读流,最重要的是管道读取函数。 超级重要的管道功能,基本上允许我们将流连接在一起,将数据从一个流传递到另一个流,而不必担心任何事件。

接下来,可写流是我们可以向其写数据的流。 因此,基本上,这与可读流相反。 一个很好的例子是我们可以发送回客户端的http响应,它实际上是可写的流。 因此,我们可以将数据写入其中的流。 因此,当我们要发送数据时,我们必须将其写入某个地方,对吗? 那个地方是可写的数据流,这很有意义,对吗?

例如,如果我们想将大视频文件发送给客户端,我们就像Netflix或YouTube一样。 现在,关于事件,最重要的是消耗事件和结束事件。 最重要的功能是写入和结束功能。

关于双工流。 它们只是同时可读写的流。 这些不太常见。 但是无论如何,一个很好的例子是来自net模块的Web套接字。 Web套接字基本上只是客户端和服务器之间的通信通道,它可以双向运行,并且在建立连接后保持打开状态。

最后,转换流是双工流,因此可读和可写的流都可以同时在读取或写入数据时修改或转换数据。 一个很好的例子是zlib核心模块,用于压缩实际上使用转换流的数据。

***节点将这些http请求和响应实现为流,然后我们可以使用它们,并可以使用可用于每种流类型的事件和函数来使用它们。 当然,我们也可以实现自己的流,然后使用这些相同的事件和功能来使用它们。

现在让我们尝试一些示例:

const fs = require('fs');
const server = require('http').createServer();
server.on('request', (req, res) =>{
    fs.readFile('./txt/long_file.txt', (err, data)=>{
        if(err) console.log(err);
        res.end(data);
    });
});
server.listen('8000','127.0.01', ()=>{
    console.log(this);
});

假设long_file.txt文件包含1000000K行,每行包含100个以上的单词,因此这是一个包含大量数据的拥抱文件,现在在上面的示例中问题是通过使用readFile()函数节点将整个文件加载到内存中,因为只有在将整个文件加载到内存节点后,才能将数据作为响应对象进行传输。

当文件很大时,以及当有大量请求发送到您的服务器时,随着时间的流逝,节点进程将很快耗尽资源,并且您的应用程序将退出工作,一切都将崩溃。

让我们尝试使用流查找解决方案:

const fs = require('fs');
const server = require('http').createServer();

server.on('request', (req, res) =>{
    const readable = fs.createReadStream('./txt/long_file.txt');
    readable.on('data', chunk=>{
        res.write(chunk);
    });
    readable.on('end',()=>{
        res.end();
    })
    readable.on('error', err=>{
        console.log('err');
        res.statusCode=500;
        res.end('File not found');
    });
});

server.listen('8000','127.0.01', ()=>{
    console.log(this);
});

在上述带有流的示例中,我们正在有效地流传输文件,正在读取文件的一部分,并在可用后立即使用响应流的write方法将其直接发送给客户端。 然后,当下一个价格可用时,将发送该价格,一直到读取整个文件并将其流式传输到客户端为止。

因此,流基本上已经完成了从文件中读取数据的操作,结束事件将发出信号,表明不再有任何数据将写入此可写流。

通过上述实践,我们解决了先前的问题,但是上述示例仍然存在巨大的问题,称为背压。

问题在于,我们使用的可读流(用于从磁盘读取文件的可读流)比通过网络通过响应可写流实际发送结果快得多。 这将使响应流不堪重负,因为响应流无法如此快地处理所有这些传入数据,因此该问题称为背压。

解决方案是使用管道运算符,它将处理数据传入和传出的速度。

const fs = require('fs');
const server = require('http').createServer();

server.on('request', (req, res) =>{
    const readable = fs.createReadStream('./txt/long_file.txt');
    readable.pipe(res);

});

server.listen('8000','127.0.01', ()=>{
    console.log(this);
});
回答4

我认为您正在考虑所有这些工作原理,我很喜欢。

哪些流适合

流对于两件事有好处:

  • 当操作缓慢时,它会在获得部分结果时为您提供部分结果。 例如,读取文件的速度很慢,因为HDD速度很慢,并且在读取文件时可以为您提供部分文件。 使用流,您可以使用文件的这些部分并立即开始对其进行处理。

  • 它们还可以将程序连接在一起(读取功能)。 就像在命令行中一样,您可以通过管道将不同的程序组合在一起以产生所需的输出。 示例: cat file | grep word cat file | grep word

他们如何在幕后工作...

这些操作中的大多数操作都需要花费一些时间,并且在获得结果时会给您部分结果,这些操作不是由Node.js完成的,而是由V8 JS引擎完成的,它只会将这些结果交给JS以便您使用。

要了解您的http示例,您需要了解http的工作原理

网页可以以不同的编码形式发送。 最初,只有一种方法。 请求时发送整个页面的位置。 现在,它具有更有效的编码来执行此操作。 其中之一被分块发送,直到发送完整个页面为止,才发送部分网页。 这很好,因为可以在收到网页时对其进行处理。 想象一下一个网络浏览器。 它可以在下载完成之前开始渲染网站。

您的.pause和.continue问题

首先,Node.js流仅在同一Node.js程序中工作。 Node.js流无法与其他服务器甚至程序中的流进行交互。

这意味着在下面的示例中,Node.js无法与Web服务器通信。 它不能告诉它暂停或继续。

Node.js <-> Network <-> Webserver

真正发生的是Node.js要求提供一个网页,然后它开始下载该网页,并且无法停止该下载。 只是丢下插座。

那么,当您在Node.js中执行.pause或.continue时,会发生什么呢?

它开始缓冲该请求,直到准备好再次使用它为止。 但是下载从未停止。

事件循环

我有一个完整的答案可以解释事件循环的工作原理,但是我认为对您来说更好。

受限制的 HTML

  • 允许的HTML标签:<a href hreflang> <em> <strong> <cite> <blockquote cite> <code> <ul type> <ol start type> <li> <dl> <dt> <dd> <h2 id> <h3 id> <h4 id> <h5 id> <h6 id>
  • 自动断行和分段。
  • 网页和电子邮件地址自动转换为链接。

相关推荐
  • 如何从Node.Js中的字符串创建流?(How to create streams from string in Node.Js?)
    问题 我使用的是ya-csv库,希望将文件或流作为输入,但是我有一个字符串。 如何将该字符串转换为Node中的流? 回答1 从节点10.17开始,stream.Readable具有一个from方法,可以轻松地从任何可迭代对象(包括数组文字)创建流: const { Readable } = require("stream") const readable = Readable.from(["input string"]) readable.on("data", (chunk) => { console.log(chunk) // will be called once with `"input string"` }) 请注意,至少在10.17和12.3之间,字符串本身是可迭代的,因此Readable.from("input string")将起作用,但每个字符发出一个事件。 Readable.from(["input string"])将为数组中的每个项目发出一个事件(在本例中为一个项目)。 还要注意,在以后的节点中(可能是12.3,因为文档说函数已被更改),所以不再需要将字符串包装在数组中。 https://nodejs.org/api/stream.html#stream_stream_可读_from_iterable_options 回答2 当@substack在
  • 从两个管道流创建Node.js流(Creating a Node.js stream from two piped streams)
    问题 如果可能的话,我想通过管道将两个Node.js流合并为一个。 我正在使用Transform流。 换句话说,我希望我的图书馆返回myStream供人们使用。 例如,他们可以写: process.stdin.pipe(myStream).pipe(process.stdout); 在内部,我正在使用可以完成某些工作的第三方vendorStream ,插入到myInternalStream包含的我自己的逻辑中。 因此,以上内容将转换为: process.stdin.pipe(vendorStream).pipe(myInternalStream).pipe(process.stdout); 我可以做这样的事情吗? 我已经尝试了var myStream = vendorStream.pipe(myInternalStream)但这显然行不通。 为了与bash打个比方,假设我想编写一个程序来检查某个流的最后一行是否存在字母h ( tail -n 1 | grep h ),我可以创建一个shell脚本: # myscript.sh tail -n 1 | grep h 然后,如果人们这样做: $ printf "abc\ndef\nghi" | . myscript.sh 它只是工作。 这是我到目前为止的内容: // Combine a pipe of two streams into
  • 如何在Node.js中一次将一个可读流传输到两个可写流中?(How to pipe one readable stream into two writable streams at once in Node.js?)
    问题 目标是: 创建文件读取流。 用管道将其传输到gzip( zlib.createGzip() ) 然后将zlib输出的读取流通过管道传输到: 1)HTTP response对象 2)和可写文件流,以保存压缩后的输出。 现在我可以降到3.1了: var gzip = zlib.createGzip(), sourceFileStream = fs.createReadStream(sourceFilePath), targetFileStream = fs.createWriteStream(targetFilePath); response.setHeader('Content-Encoding', 'gzip'); sourceFileStream.pipe(gzip).pipe(response); ...效果很好,但是我还需要将压缩后的数据保存到文件中,这样我就不需要每次都进行zip压缩,并且能够直接流式传输压缩后的数据作为响应。 那么,如何在Node中一次将一个可读流传送到两个可写流中呢? sourceFileStream.pipe(gzip).pipe(response).pipe(targetFileStream); 在Node 0.8.x中工作? 回答1 管道链接/拆分不起作用,就像您在此处尝试将其发送到两个不同的后续步骤一样: sourceFileStream
  • 使用node.js流进行错误处理(Error handling with node.js streams)
    问题 处理流错误的正确方法是什么? 我已经知道有一个“错误”事件可以听,但是我想知道有关任意复杂情况的更多详细信息。 对于初学者,要制作简单的管道链时该怎么做: input.pipe(transformA).pipe(transformB).pipe(transformC)... 以及如何正确创建这些转换之一,以便正确处理错误? 更多相关问题: 当发生错误时,“结束”事件会怎样? 永远不会被解雇吗? 有时会被解雇吗? 它取决于转换/流吗? 这里的标准是什么? 有没有通过管道传播错误的机制? 域可以有效解决此问题吗? 例子会很好。 由“错误”事件引起的错误是否具有堆栈跟踪? 有时? 绝不? 有没有办法从他们那里得到一个? 回答1 转变 转换流既可读又可写,因此确实是很好的“中间”流。 因此,有时将它们称为through流。 它们与这种方式下的双工流相似,不同之处在于它们提供了一个不错的接口来操纵数据,而不仅仅是发送数据。 转换流的目的是操纵通过流传输通过流的数据。 例如,您可能要进行一些异步调用,或者派生几个字段,重新映射一些内容,等等。 有关如何创建转换流的信息,请参见此处和此处。 您所要做的就是: 包括流模块实例化(或继承自)Transform类实现一个_transform方法,该方法采用(chunk, encoding, callback) 。 块就是您的数据。 大多数情况下
  • 如何收听对MongoDB集合的更改?(How to listen for changes to a MongoDB collection?)
    问题 我正在创建一种以MongoDB作为数据存储的后台作业队列系统。 在催生工作者来处理作业之前,我如何“监听” MongoDB集合中的插入内容? 我是否需要每隔几秒钟轮询一次,以查看自上次以来是否有任何更改,或者我的脚本有什么方法可以等待插入发生? 这是我正在从事的PHP项目,但是可以使用Ruby或与语言无关的方式随意回答。 回答1 您在想什么听起来很像触发器。 MongoDB没有对触发器的任何支持,但是有些人使用了一些技巧来“滚动自己”。 这里的关键是oplog。 在副本集中运行MongoDB时,所有MongoDB操作都会记录到操作日志(称为oplog)中。 操作日志基本上只是对数据所做的修改的运行清单。 副本集功能通过侦听此操作日志上的更改,然后在本地应用更改来实现。 这听起来很熟悉吗? 我无法在此处详细说明整个过程,它是几页的文档,但是您可以使用所需的工具。 首先是关于oplog的一些文章-简要说明-本地集合的布局(包含oplog) 您还将需要利用可尾游标。 这些将为您提供一种侦听更改而不是轮询更改的方法。 请注意,复制使用可尾游标,因此这是受支持的功能。 回答2 MongoDB具有所谓的上限集合和可尾游标,该游标使MongoDB可以将数据推送到侦听器。 capped collection本质上是一个固定大小的集合,仅允许插入。 这是创建一个的样子: db
  • 我如何开始使用Node.js [关闭](How do I get started with Node.js [closed])
    问题 关闭。 此问题不符合堆栈溢出准则。 它当前不接受答案。 想要改善这个问题吗? 更新问题,使它成为Stack Overflow的主题。 3年前关闭。 这个问题的答案是社区的努力。 编辑现有答案以改善此职位。 它目前不接受新的答案或互动。 有什么好的资源可以开始使用Node.JS? 有没有好的教程,博客或书籍? 当然,我已经访问了它的官方网站http://nodejs.org/,但是我认为他们拥有的文档不是一个很好的起点。 回答1 您可以按照以下教程开始 讲解 NodeSchool.io交互式课程节点的艺术(Node.js简介) 你好,世界 Hello World Web服务器(收费) Node.js指南使用Node.js,Express和MongoDB构建博客适用于初学者的Node.js 完全有信心地学习Node.js Node JS处理模型–具有事件循环体系结构的单线程模型 RisingStack的Node Hero系列编程社区投票选出的优秀Node.js教程 Node.js教程节点30天 开发者网站 Joyent的Node开发人员站点教程老师 影片 Node Tuts(Node.js视频教程) Node.js中的Einführung(德语) Ryan Dahl对Node.js的介绍 Node.js:异步纯度导致更快的开发使用Node.js进行并行编程带有Node
  • 如何在Node.js 0.10中立即将写入流与读取流链接起来?(How to chain write stream, immediately with a read stream in Node.js 0.10?)
    问题 下一行将从指定的url变量下载图像文件: var filename = path.join(__dirname, url.replace(/^.*[\\\/]/, '')); request(url).pipe(fs.createWriteStream(filename)); 这些行将获取该图像并将其保存到MongoDB GridFS: var gfs = Grid(mongoose.connection.db, mongoose.mongo); var writestream = gfs.createWriteStream({ filename: filename }); fs.createReadStream(filename).pipe(writestream); 像这样的链pipe抛出错误:500 Cannot Pipe。 不可管道传输。 request(url).pipe(fs.createWriteStream(filename)).pipe(writestream); 发生这种情况是因为尚未准备好读取图像文件,对吗? 我该怎么办才能解决此问题?错误:500无法管道。 不可管道传输。 使用下面的:Node.js的0.10.10,猫鼬,请求和GridFS的流库。 回答1 request(url).pipe(fs.createWriteStream(filename)
  • Webpack&gulp&Grunt 面试
    Webpack 概念: 本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。 webpack 根据模块的依赖关系进行静态分析,这些文件(模块)会被包含到 bundle.js 文件中。Webpack 会给每个模块分配一个唯一的 id 并通过这个 id 索引和访问模块。 在页面启动时,会先执行 runoob1.js 中的代码,其它模块会在运行 require 的时候再执行。 核心概念:入口entry 输出output loader 插件 plugins 入口entry: 入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。 出口(output) output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件,默认值为 ./dist。 loader loader让webpack 能够去处理那些非JavaScript 文件(webpack 自身只理解 JavaScript)
  • node.js如何从stdin中读取击键(nodejs how to read keystrokes from stdin)
    问题 是否可以在正在运行的nodejs脚本中侦听传入的击键? 如果我使用process.openStdin()并侦听其'data'事件,则将输入缓冲到下一个换行符,如下所示: // stdin_test.js var stdin = process.openStdin(); stdin.on('data', function(chunk) { console.log("Got chunk: " + chunk); }); 运行此,我得到: $ node stdin_test.js <-- type '1' <-- type '2' <-- hit enter Got chunk: 12 我想要看的是: $ node stdin_test.js <-- type '1' (without hitting enter yet) Got chunk: 1 我正在寻找一个等效于例如ruby中的getc的node.js 这可能吗? 回答1 如果切换到原始模式,则可以通过这种方式实现: var stdin = process.openStdin(); require('tty').setRawMode(true); stdin.on('keypress', function (chunk, key) { process.stdout.write('Get Chunk: ' + chunk +
  • Node.js使用单独的stdout和stderr流以交互方式生成子进程(Node.js spawning a child process interactively with separate stdout and stderr streams)
    问题 考虑下面的C程序(test.c): #include <stdio.h> int main() { printf("string out 1\n"); fprintf(stderr, "string err 1\n"); getchar(); printf("string out 2\n"); fprintf(stderr, "string err 2\n"); fclose(stdout); } 哪个应该打印一行到stdout,一行到stderr,然后等待用户输入,然后另一行到stdout,另一行到stderr。 很基本! 编译并在命令行上运行时,完成后程序的输出(接收到getchar()的用户输入): $ ./test string out 1 string err 1 string out 2 string err 2 当尝试使用带有以下代码的nodejs将这个程序作为子进程生成时: var TEST_EXEC = './test'; var spawn = require('child_process').spawn; var test = spawn(TEST_EXEC); test.stdout.on('data', function (data) { console.log('stdout: ' + data); }); test.stderr.on('data
  • 如何在流中使用ES8异步/等待?(How to use ES8 async/await with streams?)
    问题 在https://stackoverflow.com/a/18658613/779159中,示例了如何使用内置的加密库和流来计算文件的md5。 var fs = require('fs'); var crypto = require('crypto'); // the file you want to get the hash var fd = fs.createReadStream('/some/file/name.txt'); var hash = crypto.createHash('sha1'); hash.setEncoding('hex'); fd.on('end', function() { hash.end(); console.log(hash.read()); // the desired sha1sum }); // read all file and pipe it (write it) to the hash object fd.pipe(hash); 但是是否可以将其转换为使用ES8异步/等待而不是使用上述回调,但仍保持使用流的效率? 回答1 async / await仅适用于promise,不适用于流。 有一些想法可以制作一种类似于流的额外数据类型,该数据类型将具有自己的语法,但是如果有的话,这些想法是高度实验性的,我将不赘述。 无论如何
  • Node.js:如何将流读入缓冲区?(Node.js: How to read a stream into a buffer?)
    问题 我写了一个非常简单的函数,可以从给定的URL下载图像,调整图像大小并上传到S3(使用'gm'和'knox'),我不知道我是否正在正确地将流读取到缓冲区中。 (一切正常,但这是正确的方法吗?) 另外,我想了解一些有关事件循环的知识,我怎么知道该函数的一次调用不会泄漏任何内容,也不会将'buf'变量更改为另一个已经运行的调用(否则这种情况是不可能的,因为回调是匿名的职能?) var http = require('http'); var https = require('https'); var s3 = require('./s3'); var gm = require('gm'); module.exports.processImageUrl = function(imageUrl, filename, callback) { var client = http; if (imageUrl.substr(0, 5) == 'https') { client = https; } client.get(imageUrl, function(res) { if (res.statusCode != 200) { return callback(new Error('HTTP Response code ' + res.statusCode)); } gm(res)
  • How do Node.js Streams work?
    I have a question about Node.js streams - specifically how they work conceptually. There is no lack of documentation on how to use streams. But I've had difficulty finding how streams work at the data level. My limited understanding of web communication, HTTP, is that full "packages" of data are sent back and forth. Similar to an individual ordering a company's catalogue, a client sends a GET (catalogue) request to the server, and the server responds with the catalogue. The browser doesn't receive a page of the catalogue, but the whole book. Are node streams perhaps multipart messages? I like
  • 使用Node.js将视频文件流式传输到html5视频播放器,以便视频控件可以继续工作吗?(Streaming a video file to an html5 video player with Node.js so that the video controls continue to work?)
    问题 Tl; Dr-问题: 用Node.js处理将视频文件流传输到html5视频播放器以使视频控件继续工作的正确方法是什么? 我认为这与处理标头的方式有关。 无论如何,这是背景信息。 该代码有点冗长,但是非常简单。 使用Node将小视频文件流化为HTML5视频很容易 我学习了如何非常轻松地将小型视频文件流式传输到HTML5视频播放器。 使用此设置,控件可以正常工作,并且视频流完美无缺。 此处包含示例视频的完整工作代码的工作副本,可在Google文档中下载。 客户: <html> <title>Welcome</title> <body> <video controls> <source src="movie.mp4" type="video/mp4"/> <source src="movie.webm" type="video/webm"/> <source src="movie.ogg" type="video/ogg"/> <!-- fallback --> Your browser does not support the <code>video</code> element. </video> </body> </html> 服务器: // Declare Vars & Read Files var fs = require('fs'), http = require(
  • 如何关闭可读流(结束之前)?(How to close a readable stream (before end)?)
    问题 如何在Node.js中关闭可读流? var input = fs.createReadStream('lines.txt'); input.on('data', function(data) { // after closing the stream, this will not // be called again if (gotFirstLine) { // close this stream and continue the // instructions from this if console.log("Closed."); } }); 这会比以下更好: input.on('data', function(data) { if (isEnded) { return; } if (gotFirstLine) { isEnded = true; console.log("Closed."); } }); 但这不会停止阅读过程... 回答1 调用input.close() 。 它不在文档中,但是 https://github.com/joyent/node/blob/cfcb1de130867197cbc9c6012b7e84e08e53d032/lib/fs.js#L1597-L1620 显然可以完成这项工作:)实际上,它所做的事情与您的isEnded类似。 EDIT
  • 与node.js中的fs.createWriteStream关联的事件(event associated with fs.createWriteStream in node.js)
    问题 写入流时达到EOF会触发什么事件? 我的代码如下。 它是根据http://docs.nodejitsu.com/articles/advanced/streams/how-to-use-fs-create-write-stream 但是令人惊讶的是,我的“结束”事件从未被解雇。 当我检查http://nodejs.org/api/stream.html#stream_event_end时,我看到可写流在'end'上没有任何事件 var x = a1.jpg; var options1 = {'url': url_of_an_image, 'encoding': null}; var r = request(options1).pipe(fs.createWriteStream('/tmp/imageresize/'+x)); r.on('end', function(){ console.log('file downloaded to ', '/tmp/imageresize/'+x); } 如何捕获EOF事件? 回答1 2013年10月30日更新 当基础资源完成写入时,可读Steam会发出close事件。 r.on('close', function(){ console.log('request finished downloading file'); }); 但是
  • Node.js最佳实践异常处理(Node.js Best Practice Exception Handling)
    问题 几天前,我才刚开始尝试使用node.js。 我意识到只要程序中有未处理的异常,Node就会终止。 这与我所见过的普通服务器容器不同,在普通服务器容器中,当发生未处理的异常时,只有工作线程死亡,并且容器仍然能够接收请求。 这引起了一些问题: 是process.on('uncaughtException')防范的唯一有效方法吗? process.on('uncaughtException')也会在异步进程执行期间捕获未处理的异常吗? 是否存在已经构建的模块(例如发送电子邮件或写入文件),在未捕获的异常的情况下可以利用该模块? 我将不胜感激任何向我展示在node.js中处理未捕获的异常的常见最佳实践的指针/文章 回答1 更新:Joyent现在有自己的指南。 以下信息更多是摘要: 安全地“抛出”错误 理想情况下,我们希望尽可能避免未捕获的错误,因此,除了从字面上抛出错误外,我们还可以根据我们的代码体系结构使用以下方法之一安全地“抛出”错误: 对于同步代码,如果发生错误,请返回错误: // Define divider as a syncrhonous function var divideSync = function(x,y) { // if error condition? if ( y === 0 ) { // "throw" the error safely by
  • 如何在不使用阻塞stdio的情况下从node.js中的子进程传输大数据/向子进程传输大数据?(How to transfer/stream big data from/to child processes in node.js without using the blocking stdio?)
    问题 我在node.js中有一堆(子)进程,它们需要传输大量数据。 当我阅读该手册时,它说它们之间的stdio和ipc接口已阻塞,因此不会起作用。 我正在研究使用文件描述符,但是找不到从它们流式传输的方法(请参见我的其他更具体的问题,如何在节点中流式传输文件描述符/从节点流式传输文件描述符?) 我想我可能会使用网络插座,但是我担心会有不必要的开销。 我也看到了这一点,但它并不相同(并且没有答案:如何在Node.js中以非阻塞方式将大量数据从子流程发送到父流程?) 回答1 我发现了一个似乎可行的解决方案:生成子进程时,您可以传递stdio选项并设置管道以流式传输数据。 诀窍是添加一个附加元素,并将其设置为“ pipe”。 在父进程中流到child.stdio[3] 。 var opts = { stdio: [process.stdin, process.stdout, process.stderr, 'pipe'] }; var child = child_process.spawn('node', ['./child.js'], opts); // send data mySource.pipe(child.stdio[3]); //read data child.stdio[3].pipe(myHandler); 在子描述符的打开子流中,文件描述符为3。 // read
  • 如何使用Node.js将base64编码的图像(字符串)直接上传到Google Cloud Storage存储桶?(How do I upload a base64 encoded image (string) directly to a Google Cloud Storage bucket using Node.js?)
    问题 目前,我正在使用@ google-cloud / storage NPM软件包将文件直接上传到Google Cloud Storage存储桶。 这需要一些技巧,因为我只有图像的base64编码的字符串。 我必须: 解码字符串将其另存为文件将文件路径发送到以下脚本以上传到Google Cloud Storage 删除本地文件 我想避免将文件完全存储在文件系统中,因为我正在使用Google App Engine,并且如果由于某种原因删除操作不起作用,我不想使文件系统超载/将垃圾文件留在那里。 这是我的上传脚本现在的样子: // Convert the base64 string back to an image to upload into the Google Cloud Storage bucket var base64Img = require('base64-img'); var filePath = base64Img.imgSync(req.body.base64Image, 'user-uploads', 'image-name'); // Instantiate the GCP Storage instance var gcs = require('@google-cloud/storage')(), bucket = gcs.bucket('google
  • 使用Node.js执行命令行二进制文件(Execute a command line binary with Node.js)
    问题 我正在将CLI库从Ruby移植到Node.js。 在我的代码中,如有必要,我将执行几个第三方二进制文件。 我不确定如何最好地在Node中完成此操作。 这是Ruby中的一个示例,其中我调用PrinceXML将文件转换为PDF: cmd = system("prince -v builds/pdf/book.html -o builds/pdf/book.pdf") Node中的等效代码是什么? 回答1 对于更高版本的Node.js(v8.1.4),事件和调用与旧版本相似或相同,但建议使用标准的新语言功能。 例子: 对于缓冲的,非流格式的输出(您一次全部获得),请使用child_process.exec: const { exec } = require('child_process'); exec('cat *.js bad_file | wc -l', (err, stdout, stderr) => { if (err) { // node couldn't execute the command return; } // the *entire* stdout and stderr (buffered) console.log(`stdout: ${stdout}`); console.log(`stderr: ${stderr}`); })