Node js Потоковый файл без сохранения в памяти - PullRequest
1 голос
/ 12 февраля 2020

Я создаю API, который должен принимать загрузки файлов. Таким образом, пользователь может POST файл до конечной точки, файл будет отправлен на проверку на вирусы, а затем, если он очищен, будет отправлен в хранилище (вероятно, S3). До сих пор я добился этого с одной проблемой: файлы временно сохраняются в файловой системе приложений. Мне нужно разработать приложение, которое не хранит вещи в памяти. Вот мой рабочий код:

приложение. js

const express = require('express');
const bb = require('express-busboy');

const app = express();

// Busboy modules extends the express app to handle incoming files
bb.extend(app, {
    upload: true,
    path: './tmp'
});

Маршруты. js

const express = require('express');
const router = express.Router();
const fileManagementService = require('./file-management-service')();

router
.route('/:fileId')
.post(async (req, res, next) => {
    try {
        const {fileId} = req.params;
        const {files} = req;
        const response = await fileManagementService.postFile(files, fileId);

        res.status(201).json(response);
    } catch (err) {
        next(err);
    }
})

file-management-service. js

const fs = require('fs');

function createUploader() {
    // POST /:fileId
    async function postFile(data, fileId) {
        const {file} = data.file;
        const fileStream = fs.createReadStream(file);
        const scanOutput = await scanFile(fileStream); // Function scans file for viruses
        const status = scanOutput.status === 'OK';
        let upload = 'NOT UPLOADED';
        if (status) {
            upload = await postS3Object({file}); // Some function that sends the file to S3 or other storage
        }
        fs.unlinkSync(file);
        return {
            fileId,
            scanned: scanOutput,
            upload 
        };
    }

    return Object.freeze({
        postFile
    });
}

module.exports = createUploader;

Как уже упоминалось, вышеприведенное работает, как и ожидалось, файл отправляется на сканирование, а затем отправляется в корзину S3 перед возвратом ответа на постер на этот счет. Однако моя реализация express -busboy хранит файл в папке ./tmp, затем я преобразую это в читаемый поток, используя fs.createReadStream(filePath); перед отправкой его в AV и снова в функцию, которая отправляет файл в S3.

Этот API размещен в кластере kubernetes, и мне нужно избегать создания состояний. Как я могу достичь вышеупомянутого без фактического сохранения файла? Я предполагаю, что busboy получает этот файл как какой-то поток, поэтому, если он не звучит плотно, может ли он не просто оставаться потоком и передаваться через эти функции для достижения того же результата?

1 Ответ

1 голос
/ 12 февраля 2020

Вы можете использовать busboy на более низком уровне и получить доступ к его переведенному потоку чтения. Вот пример из работника автобуса c, который можно адаптировать к вашей ситуации:

http.createServer(function(req, res) {
  if (req.method === 'POST') {
    var busboy = new Busboy({ headers: req.headers });
    busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
      var saveTo = path.join(os.tmpDir(), path.basename(fieldname));
      file.pipe(fs.createWriteStream(saveTo));
    });
    busboy.on('finish', function() {
      res.writeHead(200, { 'Connection': 'close' });
      res.end("That's all folks!");
    });
    return req.pipe(busboy);
  }
  res.writeHead(404);
  res.end();
}).listen(8000, function() {
  console.log('Listening for requests');
});

Ключевая часть - это то, что я аннотировал:

    // create a new busboy instance on each incoming request that has files with it
    var busboy = new Busboy({ headers: req.headers });

    // register for the file event
    busboy.on('file', function(fieldname, file, filename, encoding, mimetype) {
      // at this point the file argument is a readstream for the data of an uploaded file
      // you can do whatever you want with this readstream such as
      // feed it directly to your anti-virus 

      // this example code saves it to a tempfile
      // you would replace this with code that sends the stream to your anti-virus
      var saveTo = path.join(os.tmpDir(), path.basename(fieldname));
      file.pipe(fs.createWriteStream(saveTo));
    });

    // this recognizes the end of the upload stream and sends 
    // whatever you want the final http response to be
    busboy.on('finish', function() {
      res.writeHead(200, { 'Connection': 'close' });
      res.end("That's all folks!");
    });

    // this gets busboy started, feeding the incoming request to busboy
    // so it can start reading it and parsing it and will eventually trigger
    // one or more "file" events
    return req.pipe(busboy);

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

...