Como fazer upload de arquivos para o Google Drive em Node.js – Parte 2 – LuizTools
Como fazer upload de arquivos para o Google Drive em Node.js - Parte 2
Escrito por Luiz Duarte em 06/12/2020
No tutorial anterior a este, eu mostrei como fazer uma aplicação console que se autentica e sobe um arquivo para o Google Drive. Nesta parte 2, vou lhe mostrar como fazer uma aplicação web que sobe e exibe arquivos de mídia variados como imagens, vídeos e até áudios para o Google Drive, exibindo em um front bem simples.
Isso pode ser extremamente útil para que você possa usar a sua conta do Google Drive como um disco para armazenar arquivos multimídia do seu site ou aplicação web escrita em Node.js.
Antes de começarmos, crie uma aplicação de exemplo com o express-generator, como mostro no vídeo abaixo, usando o EJS como view-engine.
É nela que vamos construir a nossa aplicação que se integra com o Google Drive.
Falando nele, obviamente para que você consiga acompanhar este tutorial você deve ter uma conta no Google Drive. Se você possui um Gmail, automaticamente possui uma pois seus emails são armazenados na mesma que, em seu nível gratuito, fornece 15GB de armazenamento na nuvem. Por cerca de R$70/ano você faz um upgrade para 100GB o que lhe permite salvar muita coisa de maneira rápida e segura.
Depois, visite este painel do Google Drive APIs e clique no botão "Enable Drive API" para que seja possível se integrar com a sua conta do GDrive, conforme a imagem abaixo.
Ao ativar a API, você deve informar uma URL de retorno na sua aplicação, que receberá o acess token quando o usuário fizer a integração com a conta dele. Use localhost:3000 em nosso tutorial e selecione o tipo Web Server.
Você também receberá um Client ID e um Client Secret, que serão necessários para a autenticação na aplicação Node.js. Baixe o arquivo credentials.json e salve-o na pasta do projeto que criou com o express-generator, mas não esqueça de colocar ele no seu gitignore.
Atenção: este é um projeto intermediário em Node.js. Recomendo tutoriais mais básicos caso esteja começando agora.
Agora que temos as credenciais para acessar a nossa conta do Google Drive e um projeto Express funcional, vamos começar instalando as dependências que vamos precisar.
npm i formidable googleapis
O Formidable é um pacote que eu já ensinei a usar antes aqui no blog, neste tutorial. Ele serve para fazer upload de arquivos facilmente para um backend Node.js
O GoogleAPIs é o pacote oficial do Google para integração com seus produtos, abstraindo toda a comunicação com as APIs Restful deles através de classes e funções.
Para facilitar o desenvolvimento, também recomendo a instalação do nodemon.
Agora vamos configurar o nosso front-end. Abra views/index.ejs e altere o HTML para que tenhamos 3 formulários: um para envio de imagens, outro para envio de vídeos e outro para envio de áudios.
<!DOCTYPE html> <html> <head> <title><%= title %></title> <link rel='stylesheet' href='/stylesheets/style.css' /> </head> <body> <h1><%= title %></h1> <p>Welcome to <%= title %></p> <p> <form action="auth" method="POST"> Autorizado? <%= auth %> <input type="submit" value="Autorizar" /> </form> </p> <p> <form action="images" method="POST" enctype="multipart/form-data"> <p>Pasta: <select name="folder"> <option value="1i0xy3duWqZzbBLGNEnvjYNL_Cm-CBpbF">Teste</option> </select> </p> Upload de Imagem: <input type="file" name="image" /> <input type="submit" value="Enviar" /> </form> </p> <p> <img id="image1" src="<%= image %>" width="320" height="240" /> </p> <p> <form action="videos" method="POST" enctype="multipart/form-data"> <p>Pasta: <select name="folder"> <option value="1i0xy3duWqZzbBLGNEnvjYNL_Cm-CBpbF">Teste</option> </select> </p> Upload de Vídeo: <input type="file" name="video" /> <input type="submit" value="Enviar" /> </form> </p> <p> <video id="video1" width="320" height="240" controls> <source src="<%= video %>" type="video/mp4"> Your browser does not support the video tag. </video> </p> <p> <form action="audios" method="POST" enctype="multipart/form-data"> <p>Pasta: <select name="folder"> <option value="1i0xy3duWqZzbBLGNEnvjYNL_Cm-CBpbF">Teste</option> </select> </p> Upload de Áudio: <input type="file" name="audio" /> <input type="submit" value="Enviar" /> </form> </p> <p> <audio id="audio1" controls> <source src="<%= audio %>" type="audio/mpeg"> Your browser does not support the audio tag. </audio> </p> </body> </html>
Autorizado? <%= auth %>
<input type="submit" value="Autorizar" />
<p>Pasta:
<option value="1i0xy3duWqZzbBLGNEnvjYNL_Cm-CBpbF">Teste</option>
Upload de Imagem: <input type="file" name="image" />
<img id="image1" src="<%= image %>" width="320" height="240" />
<form action="videos" method="POST" enctype="multipart/form-data">
Upload de Vídeo: <input type="file" name="video" />
<source src="<%= video %>" type="video/mp4">
<form action="audios" method="POST" enctype="multipart/form-data">
Upload de Áudio: <input type="file" name="audio" />
<source src="<%= audio %>" type="audio/mpeg">
Aqui não temos nada demais, do ponto de vista de EJS: uma página HTML com alguns formulários, botões, inputs de arquivos, etc. Mas alguns pontos merecem atenção.
Primeiro, repare que usei alguns selects com o nome de uma pasta e um código no value do option. O nome da pasta deve ser uma que você crie na sua conta do Google Drive, a minha se chamada Teste. Já o código que vai no value do option é o ID da pasta que o Google Drive gera pra você. É bem simples de descobrir o ID de uma pasta, basta entrar nela e reparar na URL.
O que vem depois de folders/ é o ID da pasta em questão. Copie esse ID e coloque no option do seu select. Mais tarde, vamos fazer upload do arquivo para dentro desta pasta específica.
Outro ponto que merece atenção são as variáveis que devem vir do servidor e temos cinco delas: audio, video, image, auth e a title, que já vem por padrão. Para conseguirmos fazer um primeiro teste, precisamos ir no arquivo routes/index.js e alterar a rota GET padrão.
/* GET home page. */ router.get('/', async (req, res, next) => { res.render('index', { title: 'Express', audio: '', video: '', image: '', auth: false }); });
audio: '',
Se você rodar seu projeto agora, ele deve abrir uma página index com os formulários e a imagem, vídeo e áudio não carregarão, pois os valores dessas variáveis vieram vazios do servidor.
Opcionalmente, você pode retornar valores arbitrários, para que já venha algo carregado.
Agora temos o projeto minimamente preparado para criarmos a integração com o Google Drive.
O primeiro passo é fazermos funcionar o botão de autenticação que deixamos bem no topo da página. Ele é justamente pra conectar a nossa web application na conta do Google Drive de quem estiver utilizando ela.
Para isso, vamos criar um módulo gdrive.js na raiz do nosso projeto, que vai encapsular todas as funções de integração com o Google Drive.
const SCOPES = ['https://www.googleapis.com/auth/drive.file']; const TOKEN_PATH = './token.json'; const fs = require('fs'); const credentials = require('./credentials.json'); const { google } = require('googleapis'); function getOAuthClient() { const { client_secret, client_id, redirect_uris } = credentials.web; return new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]); } async function getAuthorization(response) { const oAuth2Client = getOAuthClient(); if (!fs.existsSync(TOKEN_PATH)) { const url = await oAuth2Client.generateAuthUrl({ access_type: 'offline', scope: SCOPES, }); return response.redirect(url); } const token = JSON.parse(fs.readFileSync(TOKEN_PATH)); oAuth2Client.setCredentials(token); return oAuth2Client; } async function getAccessToken(request, response) { try { const oAuth2Client = getOAuthClient(); const {tokens} = await oAuth2Client.getToken(request.query.code); fs.writeFileSync(TOKEN_PATH, JSON.stringify(tokens)); return getAuthorization(response); } catch (err) { return console.error('Error retrieving access token', err); } } module.exports = { getAccessToken, getAuthorization }
const SCOPES = ['https://www.googleapis.com/auth/drive.file'];
const TOKEN_PATH = './token.json';
function getOAuthClient() {
return new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
async function getAuthorization(response) {
const oAuth2Client = getOAuthClient();
if (!fs.existsSync(TOKEN_PATH)) {
const url = await oAuth2Client.generateAuthUrl({
const token = JSON.parse(fs.readFileSync(TOKEN_PATH));
async function getAccessToken(request, response) {
const {tokens} = await oAuth2Client.getToken(request.query.code);
return getAuthorization(response);
return console.error('Error retrieving access token', err);
module.exports = { getAccessToken, getAuthorization }
A autenticação que vamos usar é a baseada em OAuth, ou seja, delegação de acesso. Assim, para nos autenticarmos vamos enviar o usuário para uma página do Google e, se ele autorizar a integração, seremos devolvidos de volta pra nossa aplicação com um access token que deveremos armazenar para uso posterior nas requests.
Assim, começamos declarando uma constante de escopo necessária para a autenticação, o caminho do arquivo de token onde vamos salvar o access token, o módulo fs para ler/escrever em arquivos, carregamos o arquivo de credenciais (que baixamos ao criar nosso projeto no painel do Google, lembra?) e o módulo da Google em Node.js.
Logo abaixo temos 3 funções, sendo que duas delas serão exportadas mais tarde, para uso na aplicação e a outra é para uso interno, a getOAuthClient, que nada mais faz do que instanciar um objeto oauth2Client com as credenciais geradas no painel do Google.
As outras duas funções, uma é para solicitar o acesso (getAuthorization) e a outra é para receber o Access Token depois do acesso concedido (getAccessToken).
A getAuthorization verifica se já temos um token de acesso. Se não tivermos, gera a URL para obter o access token e redireciona o usuário para lá. Se já tivermos, carrega junto das credenciais do nosso oauth2Client e o retorna, para usarmos nas demais funções.
Já o getAccessToken deve ser chamado no retorno da delegação de acesso, ele pega uma variável code que o Google nos envia na QueryString, gera o token a partir dela e o salva para uso posterior, chamando a getAuthorization que vai fazer o resto do trabalho.
Para ver se este nosso código está funcionando, vamos no nosso routes/index.js e vamos criar a rota de POST que vai receber o submit do formulário de autenticação que temos na nossa view.
/* POST auth */ router.post('/auth', async (req, res, next) => { const oauthClient = await gdrive.getAuthorization(res); res.send(JSON.stringify(oauthClient)); })
/* POST auth */
router.post('/auth', async (req, res, next) => {
const oauthClient = await gdrive.getAuthorization(res);
res.send(JSON.stringify(oauthClient));
Isso é o suficiente para, quando o usuário clicar no botão de Autorizar, ele será redirecionado para a página do Google para autorizar o uso do Google Drive dele.
Se você configurou sua aplicação corretamente no painel do Google, após a autorização ser concedida, o Google vai redirecionar o usuário de volta para a sua aplicação, com um monte de variáveis na query string. Então precisamos modificar o GET da rota raiz para pegar estas variáveis e finalizar a autenticação.
/* GET home page. */ router.get('/', async (req, res, next) => { let oauthClient; if (req.query.code) oauthClient = await gdrive.getAccessToken(req, res); else oauthClient = await gdrive.getAuthorization(res); res.render('index', { title: 'Express', audio: '', video: '', image: '', auth: oauthClient != null }); });
let oauthClient;
if (req.query.code)
oauthClient = await gdrive.getAccessToken(req, res);
oauthClient = await gdrive.getAuthorization(res);
auth: oauthClient != null
Com isso, se veio o code na querystring, chamamos a função de obter e armazenar o Access Token, caso contrário, tentamos obter a autorização a partir do token que já deve estar armazenado. Deste jeito, ao abrir a página inicial da aplicação, mesmo antes de clicar no botão de Autorizar, se a aplicação perceber a ausência do access token armazenado, já vai obrigar o usuário a se autenticar.
Note que alterei a lógica do res.render, para que a variável auth retorne true caso tenha conseguido carregar o objeto oauthClient corretamente.
Teste a aplicação agora, deve estar se autenticando corretamente. Repare que na raiz do seu projeto deve ter um arquivo token.json depois do acesso ter sido liberado. Também coloque esse arquivo no seu gitignore.
Upload de arquivos no Google Drive
Agora que já estamos conseguindo nos autenticar via OAuth no Google Drive do usuário, é hora de criar as funções de envio de arquivo no gdrive.js.
async function fileUpload(fileName, filePath, mimeType, folderId) { const oauthClient = await getAuthorization(); const fileMetadata = { name: fileName, parents: [folderId] }; const media = { mimeType,// "image/jpeg", body: fs.createReadStream(filePath) } const drive = google.drive({ version: 'v3', auth: oauthClient }); try { const file = await drive.files.create({ resource: fileMetadata, media: media }); return file.data.id; } catch (err) { return console.log('The API returned an error: ' + err); } } module.exports = { getAccessToken, getAuthorization, fileUpload }
async function fileUpload(fileName, filePath, mimeType, folderId) {
const oauthClient = await getAuthorization();
mimeType,// "image/jpeg",
const drive = google.drive({ version: 'v3', auth: oauthClient });
const file = await drive.files.create({
module.exports = { getAccessToken, getAuthorization, fileUpload }
Esta função é bem direta e muito semelhante à que usamos na parte 1 deste tutorial, quando criamos aquela prova de conceito via console.
Começamos obtendo a autorização, depois um objeto de metadados do arquivo contendo o nome do arquivo (só o nome) e o ID da pasta dele no Google Drive.
Depois, criamos outro objeto para o arquivo em si, onde definimos seu mime-type (tipo de arquivo) e apontamos a stream de leitura para o caminho completo do arquivo, que deve estar no seu servidor antes de ser enviado para o Google Drive.
Por fim, carregamos um objeto google.drive com o nosso objeto oauthClient e chamamos a função drive.files.create para enviar o arquivo para o Google Drive do usuário, retornando o ID gerado pelo Google.
Note que esta função espera que nosso arquivo já esteja no servidor, ou seja, teremos de fazer upload do frontend para o backend e depois do backend para o Google Drive. Sendo assim, vamos voltar no routes/index.js e vamos criar a rota de POST de uma imagem, para testarmos este envio que é o mais simples dos 3.
/* POST a new image */ router.post('/images', async (req, res, next) => { const form = new formidable.IncomingForm(); form.parse(req, async (err, fields, files) => { if (err) return console.log(err); const fileId = await gdrive.fileUpload(files.image.name, files.image.path, files.image.type, fields.folder); const imageUrl = `https://drive.google.com/uc?export=view&id=${fileId}`; res.render('index', { title: 'Express', audio: '', video: '', image: imageUrl, auth: true }); }); })
/* POST a new image */
router.post('/images', async (req, res, next) => {
const fileId = await gdrive.fileUpload(files.image.name, files.image.path, files.image.type, fields.folder);
const imageUrl = `https://drive.google.com/uc?export=view&id=${fileId}`;
res.render('index', { title: 'Express', audio: '', video: '', image: imageUrl, auth: true });
Aqui, usamos o Formidable para pegar o formulário que foi enviado, separando o que é o arquivo que foi feito upload (files) dos campos do formulário (fields) e enviarmos estas informações para nosso módulo gdrive fazer o upload.
Como resultado, teremos o ID do arquivo gerado no Google Drive e com ele montamos uma URL pública para devolver pro front renderizar.
Isso deve permitir que, ao fazer upload da image, imediatamente ela será exibida no front end ao fim da request.
Note que o usuário vê esta imagem pois ele é dono dela, mas que se você quiser que outras pessoas também consigam ver ela, terá de tornar a pasta onde ela está armazenada, em uma pasta pública.
Outro ponto que vale mencionar é que a cópia do arquivo que ficou no nosso servidor vai ser apagada automaticamente depois de um tempo, pois por padrão o Formidable sobe os arquivos para a pasta temp do seu sistema operacional.
As rotas de POST para os vídeos e áudios funcionam da mesma forma e tenho certeza que você é capaz de criá-las. Caso tenha qualquer dificuldade, use o formulário ao final deste post para ter acesso ao código-fonte deste tutorial.
Gostou deste tutorial? Quer aprender mais sobre construção de aplicações web em Node.js? Conheça o meu curso clicando no banner abaixo.