Node.js:如何将stream读入缓冲区?

我写了一个非常简单的函数,从给定的URL下载图像,resize并上传到S3(使用'gm'和'knox'),我不知道是否正确地读取stream到缓冲区。 (一切正常,但这是正确的方式?)

另外,我想了解关于事件循环的一些事情,我怎么知道函数的一个调用不会泄漏任何东西,或者将“buf”variables更改为另一个已经运行的调用(或者这种情况是不可能的,因为callback是匿名的function?)

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) .geometry(1024, 768, '>') .stream('jpg', function(err, stdout, stderr) { if (!err) { var buf = new Buffer(0); stdout.on('data', function(d) { buf = Buffer.concat([buf, d]); }); stdout.on('end', function() { var headers = { 'Content-Length': buf.length , 'Content-Type': 'Image/jpeg' , 'x-amz-acl': 'public-read' }; s3.putBuffer(buf, '/img/d/' + filename + '.jpg', headers, function(err, res) { if(err) { return callback(err); } else { return callback(null, res.client._httpMessage.url); } }); }); } else { callback(err); } }); }).on('error', function(err) { callback(err); }); }; 

总的来说,我没有看到任何会破坏你的代码。

两点build议:

组合Buffer对象的方式是不理想的,因为它必须复制每个“data”事件上的所有预先存在的数据。 把数据块放在一个数组中,最后把它们连接起来会更好。

 var bufs = []; stdout.on('data', function(d){ bufs.push(d); }); stdout.on('end', function(){ var buf = Buffer.concat(bufs); 

为了提高性能,我会考虑你正在使用的S3库是否支持stream。 理想情况下,您根本不需要创build一个大的缓冲区,而只需将stdoutstream直接传递给S3库。

至于你的问题的第二部分,这是不可能的。 当一个函数被调用的时候,它被分配了它自己的私有上下文,并且里面定义的所有东西都只能从该函数中定义的其他项中访问。

更新

将文件转储到文件系统可能意味着每个请求的内存使用量更less,但文件IO可能非常慢,因此可能不值得。 我想说,你不应该优化太多,直到你可以剖析和压力testing这个function。 如果垃圾收集器正在做它的工作,你可能会过度优化。

尽pipe如此,反正有更好的方法,所以不要使用文件。 由于所有你想要的是长度,你可以计算,而不需要将所有的缓冲区附加在一起,所以你根本不需要分配一个新的缓冲区。

 var pause_stream = require('pause-stream'); // Your other code. var bufs = []; stdout.on('data', function(d){ bufs.push(d); }); stdout.on('end', function(){ var contentLength = bufs.reduce(function(sum, buf){ return sum + buf.length; }, 0); // Create a stream that will emit your chunks when resumed. var stream = pause_stream(); stream.pause(); while (bufs.length) stream.write(bufs.shift()); stream.end(); var headers = { 'Content-Length': contentLength, // ... }; s3.putStream(stream, ....); 

一个相关的项目是node-stream-buffer 。 说明:“使用支持缓冲区的可读写stream”。

我build议在最后只有一次缓冲区和连续数组到结果缓冲区。 它很容易手动,或者可以使用节点缓冲区

我只想发布我的解决scheme。 以前的答案对我的研究很有帮助。 我使用length-stream来获取stream的大小,但问题在于callback在stream结束时被触发,所以我也使用stream-cache来cachingstream,并且在我知道的时候将其传递给res对象内容长度。 如果出现错误,

 var StreamCache = require('stream-cache'); var lengthStream = require('length-stream'); var _streamFile = function(res , stream , cb){ var cache = new StreamCache(); var lstream = lengthStream(function(length) { res.header("Content-Length", length); cache.pipe(res); }); stream.on('error', function(err){ return cb(err); }); stream.on('end', function(){ return cb(null , true); }); return stream.pipe(lstream).pipe(cache); }