Доступ к необработанному файловому потоку из загружаемого файла node

Я создаю приложение, которое принимает некоторые загрузки файлов и отправляет их прямо на S3. Я бы предпочел не иметь файл tmp на моем сервере, поэтому я использую модуль Knox и хотел бы взять необработанный поток из Formidable и отправить его через Knox на S3. Я сделал что-то подобное с помощью Knox для загрузки файла с помощью этого кода:

knox.downloads.get(widget.download).on('response',function(sres){
    res.writeHead(200, {
        'Content-Type':'application/zip',
        'Content-Length': sres.headers['content-length'],
        'Content-Disposition':'attachment; filename=' + widget.download
    });
    util.pump(sres, res);
}).end();

Теперь я хотел бы сделать что-то подобное в oposite направлении (загрузка файла из браузера на S3).

До сих пор я написал обработчик событий для захвата каждой части данных из файла при ее загрузке:

var form = new formidable.IncomingForm();
form.onPart = function(part){
    if(!part.filename){
        form.handlePart(part);
    }else{
        if(part.name == 'download'){
            // Upload to download bucket
            controller.putDownload(part);
        }else{
            // Upload to the image bucket
            controller.putImage(part);
        }
        //res.send(sys.inspect(part));
    }
}
form.parse(req, function(err, fields, files){
    if(err){
        res.json(err);
    }else{
        res.send(sys.inspect({fields:fields, files:files}), {'content-type':'text/plain'});
        //controller.createWidget(res,fields,files);            
    }
});


controller.putDownload = function(part){
    part.addListener('data', function(buffer){
        knox.download.putStream(data,part.filename, function(err,s3res){
            if(err)throwError(err);
            else{
                console.log(s3res);
            }
        });
    })
    knox.downloads.putStream(part, part.filename, function(err,s3res){

        if(err)throwError(err);
        else{
            console.log(s3res);
        }
    });
}

Но событие данных только дает мне буфер. Так можно ли захватить сам поток и нажать его на S3?

Ответ 1

Что вы хотите сделать, это переопределить метод Form.onPart:

IncomingForm.prototype.onPart = function(part) {
  // this method can be overwritten by the user
  this.handlePart(part);
};

Исключительное поведение по умолчанию заключается в том, чтобы записать деталь в файл. Вы этого не хотите. Вы хотите обрабатывать события "part" для записи в загрузку knox. Начните с этого:

form.onPart = function(part) {
    if (!part.filename) {
        // let formidable handle all non-file parts
        form.handlePart(part);
        return;
    }

Затем откройте запрос knox и самостоятельно обработайте события исходной части:

part.on('data', function(data) {
    req.write(data);
});
part.on('end', function() {
    req.end();
});
part.on('error', function(err) {
    // handle this too
});

В качестве бонуса, если req.write(data) возвращает false, что означает, что буфер отправки заполнен. Вы должны приостановить Грозный парсер. Когда вы получаете событие drain из потока Knox, вы должны возобновить Formidable.

Ответ 3

В промежуточном программном обеспечении Express я использую formidable PassThrough вместе с PassThrough для потоковой передачи файла на S3 (в моем случае на Minio, который совместим с S3 через Minio SDK; и я считаю, что он работает и для AWS S3 с тем же Minio SDK)

Вот пример кода.

const formidable = require('formidable')
const { PassThrough } = require('stream')

const form = new formidable.IncomingForm()
const pass = new PassThrough()

const fileMeta = {}
form.onPart = part => {
  if (!part.filename) {
    form.handlePart(part)
    return
  }
  fileMeta.name = part.filename
  fileMeta.type = part.mime
  part.on('data', function (buffer) {
    pass.write(buffer)
  })
  part.on('end', function () {
    pass.end()
  })
}
form.parse(req, err => {
  if (err) {
    req.minio = { error: err }
    next()
  } else {
    handlePostStream(req, next, fileMeta, pass)
  }
})

И handlePostStream выглядит так, как handlePostStream ниже:

const uuidv1 = require('uuid/v1')

const handlePostStream = async (req, next, fileMeta, fileStream) => {
  let filename = uuidv1()

  try {
    const metaData = {
      'content-type': fileMeta.type,
      'file-name': Buffer.from(fileMeta.name).toString('base64')
    }

    const minioClient = /* Get Minio Client*/
    await minioClient.putObject(MINIO_BUCKET, filename, fileStream, metaData)

    req.minio = { post: { filename: '${filename}' } }
  } catch (error) {
    req.minio = { error }
  }
  next()
}

Вы можете найти исходный код на GitHub, а также его модульные тесты.

Ответ 4

У вас нет возможности захватить поток, потому что данные должны быть переведены Formidable. buffer вам предоставляется содержимое файла в кусках buffer.length: это может быть проблемой, потому что, глядя на Formidable docs, кажется, что пока файл не будет полностью загружен, он не сможет достоверно сообщить размер файла и Knox put может понадобиться.

Никогда раньше не использовал Knox, но вам может быть повезло с чем-то вроде этого:

controller.putDownload = function(part){
    var req = knox.download.put(part.filename, {
      'Content-Type': 'text/plain'
    });
    part.addListener('data', function(buffer){
    req.write(buffer);
    });
    req.on('response', function(res){
       // error checking
    });
    req.end();
}

Немного не уверен в битах проверки ответа, но... посмотрите, можете ли вы наклеить это на фигуру. Кроме того, Поток потока октетов из запроса на S3 с knox на node.js также имеет возможность записи, которая может быть вам полезна.