Подтвердить что ты не робот

Node.js: Как читать поток в буфер?

Я написал довольно простую функцию, которая загружает изображение с заданного 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)
        .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);
    });
};
4b9b3361

Ответ 1

В целом, я не вижу ничего, что могло бы сломаться в вашем коде.

Два предложения:

То, как вы комбинируете объекты Buffer является неоптимальным, поскольку оно должно копировать все ранее существующие данные для каждого события "data". Было бы лучше поместить куски в массив и concat их в конце.

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

Что касается производительности, я бы посмотрел, поддерживает ли используемая вами библиотека S3 потоки. В идеале вам вообще не нужно создавать один большой буфер, а вместо этого просто передавать поток stdout непосредственно в библиотеку S3.

Что касается второй части вашего вопроса, это невозможно. Когда функция вызывается, ей присваивается собственный закрытый контекст, и все, что определено внутри нее, будет доступно только из других элементов, определенных внутри этой функции.

Обновить

Вывод файла в файловую систему, вероятно, будет означать меньшее использование памяти на запрос, но ввод-вывод файла может быть довольно медленным, поэтому это может не стоить этого. Я бы сказал, что вам не следует слишком много оптимизировать, пока вы не сможете профилировать и провести стресс-тестирование этой функции. Если сборщик мусора выполняет свою работу, возможно, вы чрезмерно оптимистичны.

Учитывая все вышесказанное, в любом случае есть лучшие способы, поэтому не используйте файлы. Поскольку все, что вам нужно, это длина, вы можете рассчитать ее, не добавляя все буферы вместе, поэтому вам вообще не нужно выделять новый буфер.

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, ....);

Ответ 2

Вы можете легко сделать это, используя node-fetch, если вы извлекаете из http (s) URI.

Из readme:

fetch('https://assets-cdn.github.com/images/modules/logos_page/Octocat.png')
    .then(res => res.buffer())
    .then(buffer => console.log)

Ответ 3

Связанный проект node-stream-buffer. Описание: "Чтение и запись потоков, использующих буферные буферы".

Ответ 4

Я предлагаю иметь массив буферов и concat для результирующего буфера только один раз в конце. Его легко сделать вручную, или можно использовать node-buffers

Ответ 5

Я просто хочу опубликовать свое решение. Предыдущие ответы были очень полезны для моих исследований. Я использую stream-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);
}

Ответ 6

Я предлагаю метод 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

          });

Когда я сделал строковый метод, я получил эту ошибку от npm jimp

buffer:  <Buffer 00 00 00 00 00>
{ Error: Could not find MIME for Buffer <null>

в основном я думаю, что приведение типа от двоичного к строковому не сработало так хорошо.

Ответ 7

Я решил такую проблему, используя stream-json

Я думаю, это может работать и для вас