How to upload files with Multer to two different storages?

Asked

Viewed 347 times

2

Good evening, I need to upload a file to a local folder (where it will be treated to compress the file) and to Storage S3. S3 configuration and local multer storage is programmed and both work separately, when I put the two settings on the same route returns Undefined in the second middleware of the multer.

Form sent:

var formulario = new FormData();

formulario.append('file', arquivo);

api.post('/postarArte', formulario).then((resposta) => {
    console.log(resposta);
});

route with the middleware:

const multer = require('multer');
const multerConfig = require('./config/multerS3');
const multerLocal = require('./config/multerLocal');

// Rota para postar arte individual
router.post('/postarArte',   

    // Upload para a pasta local
    multerLocal.single('file'),

    // Upload para o storage S3
    multer(multerConfig).single('file'),

    // inserção no banco de dados
    postarArte
    
);

i need the return of the image URL uploaded to S3 and the image path stored locally to record both in the database, so I thought I would use the same route. there is some specific setting to use two storages in multer (maybe in the same configuration file) or some other way that solves this problem?

Configuration of the multerLocal:

const multer = require('multer');
const path = require('path');

// Vamos exportar nosso módulo multer, executando com as nossas configurações em um objeto.
module.exports = (multer({

    // Como vai ser feito o armazenamento de aruqivos:
    storage: multer.diskStorage({

        // Destino do arquivo:
        destination: (req, file, cb) => {

            // setando o destino como segundo paramêtro do callback
            cb(null, path.resolve(__dirname, '..', '..', 'images'));
        },

        // Como os arquivos vão ser chamados:
        filename: (req, file, cb) => {

            // Setando o nome do arquivo que vai ser salvado no segundo paramêtro
            // Apenas concatenei a data atual como o nome original do arquivo, que a biblioteca nos disponibiliza.
            cb(null, Date.now().toString() + '-' + file.originalname);

        },

        // Formatos aceitos:
        fileFilter: (req, file, cb) => {

            const isAccepted = ['image/png', 'image/jpg', 'image/jpeg'].find(formatoAceito => {
                formatoAceito == file.mimetype
            });

            // Formato aceito:
            if (isAccepted) {

                return cb(null, true);
            }

            // Formato inválido:
            return cb(null, false);
        }

    }),

  
}));

Configuration of the multerS3


const crypto = require('crypto');

const aws = require('aws-sdk');

const multerS3 = require('multer-s3');

const storageTypes = {

    s3: multerS3({

        s3: new aws.S3(),
        bucket: '*****',
        contentType: multerS3.AUTO_CONTENT_TYPE,
        /** permissão, para que todo mundo consiga ler os arquivos */
        acl: 'public-read',
        /** nome da imagem que vai ser gravada no S3 */
        key: (req, file, callBack) => {

            crypto.randomBytes(16/** Tamanho do numero de bytes */, (err, hash) => {
                if (err) callBack(err);

                const fileName = `${hash.toString('hex')}-${file.originalname}`;
                callBack(null, fileName);
            });
        },
    }),
}

module.exports = {

    storage: storageTypes[process.env.STORAGE_TYPE],

    fileFilter: (req, file, callBack) => {
        const allowedMimes = [
            'image/jpeg',
            'image/pjpeg',
            'image/png',
            'image/gif'
        ];

        if (allowedMimes.includes(file.mimetype)) {
            callBack(null, true);
        } else {
            callBack(new Error('Arquivo inválido'));
        };
    },
    
};

Configuration of the post function:

const conexao = require('../models/conexao');

function postarArte(req, resultado) {
    console.log(req.body);
    console.log(req.file);
    
    const { titulo, desc: descricao, tipo } = req.body;

    const { 
        originalname: nomeOriginal, 
        location: url, 
        key: chave, 
        size: tamanhoArquivo 
    } = req.file;

    valores = [
        [titulo, nomeOriginal, chave, descricao, tipo, url, tamanhoArquivo]
    ]

    conexao.query(`INSERT INTO postagem(titulo, nomeOriginal, chave, descricao, tipo, url, tamanhoArquivo) VALUES (?)`, valores, (req, res) => {
        return resultado.json()
    });    

};

module.exports = postarArte

Thank you all!

  • 1

    Have you ever tried a midleware of yours where you run the two multer passing him the request?

  • 1

    Could you put in the full code? So it’s hard to understand. I’d like to see the full code of the function statement multerLocal, multerConfig, multer and the function postarArte.

  • @Sergio It’s a good idea but I don’t know how I recover the form data through a function ex: (req, res, next) => .... i don’t know where the form data is in req.body or "req.form" (something like this I imagine) have any idea about it?

  • @Danizavtz I edited the question with the configuration of the other files.

  • 1

    What are you using on Node? native code, express or other library?

  • @Sergio I’m using the express.

  • What Arrow Function do you mean?

Show 2 more comments

1 answer

2

Instead of letting the woman run "alone" you can create your middleware and intercept the answers of the woman. I assume the problem is that the multer overwrites the req.files, I’ve never tested the mullet chained up that way but I assume the problem is that it overwrites the req.files.

You can do it like this, it’s kind of callback Hell, and kind of "dirty" because it inserts keys into the object req (but I don’t see it as a namespace problem), but it does what you’re looking for:

const multerLocal = multerLocal.single('file');
const multerS3 = multer(multerConfig).single('file');

router.post('/postarArte', (req, res, next) => {
  // chama o primeiro multer
  multerLocal(req, res, () => {
        // guarda o endereço do ficheiro
        req.multerLocalFiles = req.files;
 
        // chama o segundo multer (sequencialmente)
        multerS3(req, res, () => {

          // guarda o endereço do ficheiro
          req.multerS3Files = req.files;
          // deixa o express ir para o próximo middleware
          next();
        })
      }
    },

    // inserção no banco de dados
    postarArte
);

  • I did a test and noticed that the multerS3 does not run it enters the Arrow Function but does not execute the multerConfig configuration file. I could not understand why. But thanks for the answer gave me some ideas I’m testing.

  • @Matheusbraga: what Arrow Function refers to?

  • @Serio essa: multerS3(req, res, () => ...

  • 1

    @Matheusbraga sorry for the slow response, a lot of work... What gives you console.log(req.files); within each of these Arrow functions?

  • I apologize for the same! kkk ta tranquilo. Log req.files from the first and second functions: [link] (https://github.com/bragaus/planoartistico-backend/blob/master/consoleLogArrowFunctions.txt)

Browser other questions tagged

You are not signed in. Login or sign up in order to post.