Express, realizando upload com multer
Realizar o upload é um processo corriqueiro nas aplicações web. Neste post mostrarei como realizar este procedimento utilizando Express e o middleware Multer.
Estrutura de pastas e arquivos
Antes de continuar, tenha certeza de estar usando Node.js 8.0 ou superior.
Vamos criar a pasta upload-arquivo
com a seguinte estrutura e arquivos:
upload-arquivo
├── uploads
├── public
│ └── index.html
└── server.js
A página index.html
O conteúdo de upload-arquivo/public/index.html
será um formulário apontado para o endereço file/upload
que ainda não existe em nosso servidor.
Um ponto a destacar é a propriedade encType
para indicar o tipo de codificação utilizado no envio dos dados, em nosso caso multipart/form-data
. Através de um <input type="file">
selecionamos o arquivo que desejamos enviar. Por fim, o <input type="submit">
submeterá o formulário e por conseguinte enviará os dados do nosso arquivo:
<!-- public/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Express, upload de arquivos com Multer</title>
</head>
<body>
<h1>Express, upload de arquivos com Multer</h1>
<form action='file/upload' method='post' encType="multipart/form-data">
<input type="file" name="file" />
<input type='submit' value='Upload!' />
</form>
</body>
</html>
Agora que já temos index.html
, podemos baixar as dependências necessárias para criar nosso servidor.
Baixando as dependências da aplicação
Dentro da pasta upload-arquivo
criaremos o arquivo package.json
e instalaremos o Express e o Multer para nos ajudar a criar nosso servidor e a lidar com o upload de arquivo respectivamente. Para isso, basta executarmos dentro da pasta upload-arquivo
a sequência de comandos:
npm init -y
npm i -S express@4.15.4 multer@1.3.0
A pasta upload-arquivo
estará assim:
├── uploads
├── node_modules
├── package-lock.json
├── package.json
├── public
│ └── index.html
└── server.js
Ótimo. Agora precisamos tornar a pasta public
acessível através do navegador para que ele consiga carregar index.html
.
Compartilhando a pasta estática
Já vimos o processo de tornar uma pasta estática acessível para o navegador no post Streaming de audio com Node.js, mas recordar é viver, não é mesmo?
Agora, em upload-arquivo/server.js
vamos configurar o Express para compartilhar a pasta estática public
:
// server.js
const express = require('express')
, app = express();
app.use(express.static('public'));
app.listen(3000, () => console.log('App na porta 3000'));
Já podemos testar o servidor. Basta executarmos o comando node server
dentro da pasta upload-arquivo
para que nosso servidor fique de pé. Vamos acessar o endereço http://localhost:3000
e verificar se o título “Express, upload de arquivos com Multer” é exibido no navegador. Depois de testarmos, podemos parar o servidor. Ainda há mais código para ser escrito.
Configurando multer
Agora que já temos nossa pasta estática configurada, criaremos uma instância do Multer configurada para considerar a pasta uploads/
como pasta de destino dos uploads realizados:
// server.js
const express = require('express')
, app = express()
, multer = require('multer');
const upload = multer({ dest: 'uploads/' });
app.use(express.static('public'));
app.listen(3000, () => console.log('App na porta 3000'));
A ideia agora é utilizarmos essa instância para todas as rotas que precisem lidar com o upload do arquivo. Vamos criar a rota file/upload
:
// server.js
const express = require('express')
, app = express()
, multer = require('multer');
// cria uma instância do middleware configurada
const upload = multer({ dest: 'uploads/' });
app.use(express.static('public'));
// rota indicado no atributo action do formulário
app.post('/file/upload', upload.single('file'),
(req, res) => res.send('<h2>Upload realizado com sucesso</h2>'));
app.listen(3000, () => console.log('App na porta 3000'));
Veja que para a rota /file/upload
, antes de lidarmos com req
e res
, passamos o middleware do multer como parâmetro. O upload.sigle('file')
indica que estamos interessado no dado enviado com o name
file
, o mesmo name utilizado pelo <input type="file" name="file">
.
Experimente fazer o upload de um arquivo qualquer. Você verificará que ele será salvo na pasta upload-arquivo/uploads
com um nome diferente do name
definido pelo <input type="file">
, algo como 08e36ff4c9d3dc106e3a9fa2367797c9
. Se quisermos o valor do name
precisamos criar um storage.
Multer Storage
Um storage nos permite um controle mais fino do processo de upload, permitindo que extraiamos o valor do name
enviado pelo formulário através da propriedade fieldName
:
// server.js
const express = require('express')
, app = express()
, multer = require('multer');
// cria uma instância do middleware configurada
// destination: lida com o destino
// filenane: permite definir o nome do arquivo gravado
const storage = multer.diskStorage({
destination: function (req, file, cb) {
// error first callback
cb(null, 'uploads/');
},
filename: function (req, file, cb) {
// error first callback
cb(null, file.fieldname + '-' + Date.now())
}
});
// utiliza a storage para configurar a instância do multer
const upload = multer({ storage });
app.use(express.static('public'));
// continua do mesma forma
app.post('/file/upload', upload.single('file'),
(req, res) => res.send('<h2>Upload realizado com sucesso</h2>'));
app.listen(3000, () => console.log('App na porta 3000'));
Salvando com a mesma extensão do arquivo
Qualquer arquivo que realizarmos o upload será salvo no disco com o nome file-
seguido da data atual. Se quisermos manter a extensão do arquivo original podemos alterar nosso código para:
// server.js
// não esqueça de importar o módulo path!
const express = require('express')
, app = express()
, multer = require('multer')
, path = require('path');
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, `${file.fieldname}-${Date.now()}.${path.extname(file.originalname)}`);
}
});
// código posterior omitido
Com auxílio do módulo path
e com a propriedade file.originalname
conseguimos extrair a extensão do arquivo.
Salvando com o mesmo nome do arquivo
Pode ser que queiramos simplesmente manter o nome do arquivo original. Basta passarmos para cb
o valor de file.originalname
diretmente:
// server.js
// código anterior omitido
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, file.originalname);
}
});
// código posterior omitido
Conclusão
Realizar um upload na plataforma Node.js não precisa ser um martírio. Com ajuda do Express e o middlware Multer conseguimos implenentar uploads sem muito mistério.