Node.js:如何将流读入缓冲区?
我编写了一个非常简单的函数,从给定的URL下载一个图像,调整其大小并上传到S3(使用'gm'和'knox'),我不知道我是否正确地将流读取到缓冲区。(一切正常,但这是正确的方式吗?) 另外,我想了解一些关于事件循环的情况,我如何知道函数的一次调用不会泄漏任何内容或将'buf'变量更改为另一个已在运行的调用(或者由于回调是匿名函数,所以这种情况是不可能的?)Node.js:如何将流读入缓冲区?,node.js,Node.js,我编写了一个非常简单的函数,从给定的URL下载一个图像,调整其大小并上传到S3(使用'gm'和'knox'),我不知道我是否正确地将流读取到缓冲区。(一切正常,但这是正确的方式吗?) 另外,我想了解一些关于事件循环的情况,我如何知道函数的一次调用不会泄漏任何内容或将'buf'变量更改为另一个已在运行的调用(或者由于回调是匿名函数,所以这种情况是不可能的?) 我建议在最后只使用一次缓冲区数组和结果缓冲区的concat。手动操作很容易,或者可以使用总之,我看不到任何会破坏代码的东西 两项建议: 组合
我建议在最后只使用一次缓冲区数组和结果缓冲区的concat。手动操作很容易,或者可以使用总之,我看不到任何会破坏代码的东西 两项建议: 组合
Buffer
对象的方式是次优的,因为它必须在每个“数据”事件上复制所有预先存在的数据。最好将块放在一个数组中,并将它们全部放在末尾
var bufs = [];
stdout.on('data', function(d){ bufs.push(d); });
stdout.on('end', function(){
var buf = Buffer.concat(bufs);
}
为了提高性能,我将研究您使用的S3库是否支持流。理想情况下,您根本不需要创建一个大的缓冲区,而是直接将stdout
流传递到S3库
至于你问题的第二部分,那是不可能的。当调用一个函数时,它会被分配自己的私有上下文,而其中定义的所有内容都只能从该函数中定义的其他项访问
更新
将文件转储到文件系统可能意味着每个请求使用更少的内存,但文件IO可能非常慢,因此可能不值得这样做。我想说的是,在你能够分析和压力测试这个函数之前,你不应该优化太多。如果垃圾收集器正在执行其工作,则可能是过度优化
尽管如此,还是有更好的方法,所以不要使用文件。由于您只需要计算长度,无需将所有缓冲区附加在一起即可计算长度,因此根本不需要分配新的缓冲区
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, ....);
我只想发布我的解决方案。以前的答案对我的研究很有帮助。我使用length stream来获取流的大小,但这里的问题是回调是在流的末尾附近触发的,因此我还使用stream cache来缓存流,并在知道内容长度后将其传输到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);
}
如果您正在从http(s)URI中提取,则可以轻松地使用 自述文件:
fetch('https://assets-cdn.github.com/images/modules/logos_page/Octocat.png')
.then(res => res.buffer())
.then(buffer => console.log)
我建议使用loganfsmyths方法,使用数组来保存数据
var bufs = [];
stdout.on('data', function(d){ bufs.push(d); });
stdout.on('end', function(){
var buf = Buffer.concat(bufs);
}
在我当前的工作示例中,我正在使用GRIDfs和npm的Jimp
var bucket = new GridFSBucket(getDBReference(), { bucketName: 'images' } );
var dwnldStream = bucket.openDownloadStream(info[0]._id);// original size
dwnldStream.on('data', function(chunk) {
data.push(chunk);
});
dwnldStream.on('end', function() {
var buff =Buffer.concat(data);
console.log("buffer: ", buff);
jimp.read(buff)
.then(image => {
console.log("read the image!");
IMAGE_SIZES.forEach( (size)=>{
resize(image,size);
});
});
我做了一些其他的研究
使用字符串方法,但不起作用,因为我正在读取图像文件,但数组方法起作用
const DISCLAIMER = "DONT DO THIS";
var data = "";
stdout.on('data', function(d){
bufs+=d;
});
stdout.on('end', function(){
var buf = Buffer.from(bufs);
//// do work with the buffer here
});
当我使用string方法时,我从npm jimp得到了这个错误
缓冲区:
{错误:找不到缓冲区的MIME
基本上,我认为从二进制到字符串的类型转换不太好
因此:
getBufferFromStream(流:部分| null):承诺{
如果(!流){
抛出“文件流空”;
}
回报新的承诺(
(r,j)=>{
让buffer=buffer.from([]);
stream.on('data',buf=>{
buffer=buffer.concat([buffer,buf]);
});
stream.on('end',()=>r(buffer));
流.on('error',j);
}
);
}
您可以将可读流转换为缓冲区,并以如下异步方式将其集成到代码中
async streamToBuffer (stream) {
return new Promise((resolve, reject) => {
const data = [];
stream.on('data', (chunk) => {
data.push(chunk);
});
stream.on('end', () => {
resolve(Buffer.concat(data))
})
stream.on('error', (err) => {
reject(err)
})
})
}
使用方法非常简单,如下所示:
// usage
const myStream // your stream
const buffer = await streamToBuffer(myStream) // this is a buffer
您可以在res.headers处检查“content length”标题。它将给出您将接收的内容的长度(它将发送多少字节的数据)Javascript代码段
打字脚本片段
异步函数stream2buffer(stream:stream):承诺{
返回新承诺((解决、拒绝)=>{
设_buf=Array()
stream.on('data',chunk=>\u buf.push(chunk))
stream.on('end',()=>resolve(Buffer.concat(_buf)))
on('error',err=>reject('error converting stream-${err}'))
})
}
它支持流,但我需要知道S3头的内容长度,这在streamsbtw中是不可能的-问题的第二部分呢?将流从“gm”传输到一个文件,然后从该文件打开一个流并上传到S3,使用文件大小作为内容长度,这是更好的做法吗?据我所知,是不是他的方法消除了像我现在所做的那样将整个文件加载到内存中的情况,只是想指出,bufs.pop()
调用应该是bufs.unshift()
,甚至更简单,只要用一个简单的for循环替换整个while循环即可。@Bergur True,但您必须维护两个单独的累加器变量。我更喜欢维护单个变量,然后再计算长度。我不相信它会在性能或任何方面产生明显的差异。您也可以滥用响应e
从节点获取缓冲区以从任何流获取缓冲区,而不仅仅是http:新响应(stream)。buffer()
响应。buffer不是一个函数。所以,嗯……什么?编辑:响应。arrayBuffer
似乎工作得非常好……这就是我正在寻找的MVP!谢谢!
async streamToBuffer (stream) {
return new Promise((resolve, reject) => {
const data = [];
stream.on('data', (chunk) => {
data.push(chunk);
});
stream.on('end', () => {
resolve(Buffer.concat(data))
})
stream.on('error', (err) => {
reject(err)
})
})
}
// usage
const myStream // your stream
const buffer = await streamToBuffer(myStream) // this is a buffer
function stream2buffer( stream ) {
return new Promise( (resolve, reject) => {
let _buf = []
stream.on( 'data', chunk => _buf.push(chunk) )
stream.on( 'end', () => resolve(Buffer.concat(_buf)) )
stream.on( 'error', err => reject( err )
})
}
async function stream2buffer( stream:Stream ):Promise<Buffer> {
return new Promise<Buffer>( (resolve, reject) => {
let _buf = Array<any>()
stream.on( 'data', chunk => _buf.push(chunk) )
stream.on( 'end', () => resolve(Buffer.concat(_buf)) )
stream.on( 'error', err => reject( `error converting stream - ${err}`) )
})
}