1. ホーム
  2. node.js

Node.js:ストリームをバッファに読み込むには?

2023-08-20 15:07:45

質問

私は与えられたURLから画像をダウンロードし、それをリサイズしてS3にアップロードする非常に簡単な関数を書きました(「gm」と「knox」を使用)、私はバッファへのストリームの読み取りを正しく行っているかどうか全く分かりません。(全て動作していますが、正しい方法なのでしょうか?)

また、私はイベントループについて何かを理解したいのですが、関数の1つの呼び出しが何かを漏らしたり、すでに実行中の別の呼び出しに '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)
        .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);
    });
};

どのように解決するのですか?

全体として、あなたのコードで壊れるようなものは何も見当たりません。

2つの提案です。

あなたが組み合わせている方法 Buffer オブジェクトを組み合わせる方法は、'data' イベントごとに既存のデータをすべてコピーしなければならないので、最適とは言えません。チャンクを配列にして concat を使う方が良いでしょう。

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

パフォーマンスのために、私はあなたが使っているS3ライブラリがストリームをサポートしているかどうかを調べます。理想的には、1つの大きなバッファを作成する必要は全くなく、単に stdout ストリームを直接S3ライブラリに渡すのが理想的です。

質問の2番目の部分に関しては、それは可能ではありません。関数が呼び出されると、その関数は独自のプライベートコンテキストを割り当てられ、その内部で定義されたすべてのアイテムは、その関数内で定義された他のアイテムからしかアクセスできなくなります。

更新

ファイルをファイルシステムにダンプすれば、おそらく 1 リクエストあたりのメモリ使用量は少なくなりますが、ファイル 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, ....);