upload-nodejs-rest-api-como-fazer

API’s REST são muito legais para CRUD, mas e quando precisa fazer um Upload? Hoje veremos como enviar arquivos para uma API REST feita com NodeJs

O resultado final backend e frontend está disponível aqui: neste repositório do github.

Analisando a demanda

  • Necessidade Permitir o envio de arquivos em uma API REST, com frontend independente, que poderia ser qualquer client, ou seja: FrontEND agnóstica.

Servidor básico

Poderiamos facilmente implementar em nossa API RESTfull com autenticação JSON Web Token JWT, mas para evitar confusão, vamos usar um servidor bem simples para este artigo.

Primeiro, crie uma pasta para seu projeto. Depois, crie os seguintes arquivos:

[package.json][crie o arquivo e insira o código]
1
2
3
4
5
6
7
8
9
10
{
"name": "teste-upload-nodejs",
"version": "1.0.0",
"main": "server.js",
"dependencies": {
"body-parser": "^1.11.0",
"connect-multiparty": "^1.2.5",
"express": "^4.11.2",
}
}

(Iremos usar o connect-multiparty para receber o arquivo.)

Acesse a pasta com o terminal, e rode:

1
npm install

Agora vamos criar o server. Ele é bem simples, e após o código explico o que está acontecendo:

[server.js][crie o arquivo e insira o código]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var express = require('express')
, app = express()
, bodyParser = require('body-parser')
, multiparty = require('connect-multiparty');

app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
var port = process.env.PORT || 8080;
var router = express.Router();

app.use('/api', router);
/*insira as rotas aqui */

app.listen(port);

console.log('conectado a porta ' + port);

No início, temos os vars e require(), aí declaramos nossos middlewares do bodyParser.urlencoded() e bodyParser.json(). São o básico para uma API REST, sendo o primeiro para usar os URL params, e o segundo para .json()., Aí a porta, o router e um app.use('/api') que define nosso base endpoint.

Agora, criaremos nossa rota de upload. Insira, abaixo do comentário

[server.js][insira abaixo do comentário]
1
2
3
4
5
(...)
/*insira as rotas aqui */
router.route('/upload')
.post(multiparty(), require('./controllers/upload'));
(...)

Note a sintaxe do .post(multiparty(), require(.....
O que isso quer dizer, é o middleware multiparty() será executado antes de acessar o controller da rota, que iremos criar agora:

Criando o controller de Upload

Crie uma pasta chamada /controllers.
Crie um arquivo upload.js na pasta controllers

[uploads.js] [crie arquivo na pasta CONTROLLERS]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
var fs = require('fs');

module.exports = function(req, res){
res.setHeader("Access-Control-Allow-Origin", "*");
var arquivo = req.files.file;
var temporario = req.files.file.path;
var novo = './uploads/' + req.files.file.name;
fs.rename(temporario, novo, function(err){
if(err){
res.status(500).json({error: err})
}
res.json({message: "enviado com sucesso.", file: novo});
})
}

Antes de tudo, no começo da função, res.setHeader("Access-Control-Allow-Origin", "*"); vai garantir que não tenhamos problemas com CORS, deixando claro que qualquer um pode fazer requests para essa rota. Não é a melhor prática do mundo, mas pra agora, nos serve.

Daí, usamos o fs, módulo nativo do NodeJs.
O module.exports contém nossa função.
Ela recebe 2 parâmetros: req e res, nossas conhecidas.

Na propriedade files do objeto da requisição temos o nosso arquivo recebido pelo multiparty.

Usamos a fs.rename() para transferir ele da pasta temporária (req.files.file.path) para uma nova que definimos na variável var novo.

Se tiver erro, retorna res.status(500) e a mensagem em JSON.

Se der tudo certo, res.json() com o nome do arquivo.

Testando nosso server

Para testar, vá a pasta do server com o terminar e rode

1
node server

Deu certo? Show! Agora vamos fazer o frontEnd.

Server pronto, vamos ao FrontEnd.

Crie uma outra pasta qualquer e crie um arquivo index.html dentro dela.

[index.html]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<title>Simples file upload com RestAPI</title>
</head>
<body>
<script src="enviaArquivo.js" type="text/javascript"></script>

<input type="file" id="arquivoInput" />
<button onclick="enviar()">Enviar </button> <br>
Retorno JSON: <span id="mensagem"></span>
</body>
</html>

Aqui, nada demais: um <input type="file" com um ID e um <button> com onClick=enviar(), que é a função que vamos criar agora.

Crie o arquivo enviaArquivo.js, e insira:

[enviaArquivo.js] [Crie o arquivo e insira o código:]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function enviar(){
var formData = new FormData();
var arquivo = document.getElementById("arquivoInput").files[0];
formData.append("file", arquivo);
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
var div = document.getElementById('mensagem');
var resposta = xhr.responseText;
div.innerHTML += resposta;
}
}
xhr.open("POST", "http://localhost:8080/api/upload");
xhr.send(formData);
}

Aí é bem fácil:

  1. var formData = new FormData()… aí instanciamos um novo form, inserindo com append() um item chamado ‘file’ que vem do getElementById("arquivoInput"), o ID do nosso campo de arquivo no HTML.

  2. na var xhr criamos um new XMLHttpRequest(), que é a base do $ajax, mas quis fazer sem JQuery esse exemplo (mandei mal? diga nos comentários!).

  3. Definimos como propriedade do onreadystatechange do XHR que criamos uma função. Ela vai testar se xhr.readyState == 4. 4 significa concluído.
    Se sim, inserimos a resposta (xhr.responseText) no elemento #mensagem do nosso HTML.

  4. Por último, abrimos a conexão com xhr.open, fazendo um POST para a url alí definida (no caso, localhost:8080). Enviamos nosso formulário com xhr.send(formData).

Importante!!!

Não esqueça de criar a pasta /uploads na raíz do seu projeto, senão vc receberá o erro:

1
{"error":{"errno":34,"code":"ENOENT" .......

Testando nosso envio de arquivos com NodeJS

Rode o servidor pelo terminal com node server (ou já deixou rodando? tanto faz ^_^) e abra o arquivo index.html na raça mesmo, com 2 cliques. Tenta enviar um arquivo….

Viu a resposta? Maneiro né ^_^, enviamos nosso arquivo!

Para ver o arquivo, entre na pasta uploads, criada agora na pasta do seu servidor.

Não deixamos a pasta uploads pública, por isso não dá para acessar pela URL.

Deixando a pasta uploads pública

Se quiser fazer isso, basta adicionar o seguinte, antes linha app.use('/api'):

[server.js] [insira a linha à seguir]
1
app.use('/enviadas', express.static(__dirname  + '/uploads'));

Agora, acessando localhost:8080/enviadas/NOMEDOARQUIVO.ext, você pode baixar o arquivo enviado.

Conclusão

É isso aí amiguinhos! Nossa brincadeira de hoje é muito útil, e facilmente implementável. O FrontEnd, reforçando, era só um exemplo. Você pode fazer o envio usando XTHMLRequests multipart com qualquer frontend.

O resultado final deste artigo, backend e frontend, está disponível aqui: neste repositório do github.

Mais pra frente faremos algo mais bonitinho :)

Muito obrigado pela visita, e um grande abraço. Volte sempre. Keep Coding.

Ah, lembrando que agora temos Maillist. Cadastre seu e-mail na caixinha persistente ao lado –> e receba todo dia o post novo. Estou participando do #1postperday, brilhante iniciativa do @fdaciuk e tentarei fazer juz, produzindo bastante conteúdo legal