Back-End

26 mar, 2019

Como fazer upload de arquivos para o Google Drive em Node.js

Publicidade

Com meu curso de Node.js e MongoDB chegando aos quase 200 inscritos na data em que escrevo este artigo, muitas ideias têm chegado a mim, com relação a temas para artigos e novas aulas no curso.

Recentemente, um aluno estava tentando fazer algo que me chamou a atenção: uma API em Node.js que recebesse um arquivo e o enviasse para o Google Drive.

Assim, no artigo de hoje quero mostrar como você pode criar uma integração do Node.js com o Google Drive facilmente.

Vamos enviar imagens, mas você pode facilmente adaptar o código para enviar outro tipo de arquivo.

Vamos lá!

Google Drive

Obviamente, para que você consiga acompanhar o passo a passo, você deve ter uma conta no Google Drive. Se você possui um Gmail, automaticamente possui uma, pois seus e-mails são armazenados na mesma que, em seu nível gratuito, oferece 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.

Caso não possua uma, crie em drive.google.com.

Surpreendentemente, existe todo um suporte da Google à tecnologia Node.js, que você confere neste painel do Google Drive APIs.

Acesse o painel 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.

Google Drive APIs

Ao ativar a API, você receberá um Client ID e um Client Secret, que serão necessários para a autenticação na aplicação Node.js.

Guarde-os consigo, pois eu não vou fornecer os meus. Também lhe é oferecido um arquivo de credenciais (credentials.json). Baixe e salve na pasta do projeto que vamos criar a seguir.

Criando o projeto em Node

Crie uma pasta na sua máquina chamada gdrive-node e navegue até ela via terminal. Crie um gdrive-auth.js vazio e rode um ‘npm init’ para criar uma aplicação. Depois, instale a biblioteca do GDrive usando o comando abaixo:

npm install googleapis@27 --save

Lembre-se de ter o seu arquivo credentials.json dentro da pasta do projeto, pois agora vamos carregá-lo em nossa aplicação. Abra o gdrive-auth.js e inclua o seguinte código, adaptado da documentação do Google Drive APIs:

//index.js
const fs = require('fs');
const readline = require('readline');
const {google} = require('googleapis');

// If modifying these scopes, delete token.json.
const SCOPES = ['https://www.googleapis.com/auth/drive.metadata.readonly'];
// The file token.json stores the user's access and refresh tokens, and is
// created automatically when the authorization flow completes for the first
// time.
const TOKEN_PATH = 'token.json';

function callGDriveApi(callback){
    // Load client secrets from a local file.
    fs.readFile('credentials.json', (err, content) => {
    if (err) return console.log('Error loading client secret file:', err);
    // Authorize a client with credentials, then call the Google Drive API.
        if(callback)
            authorize(JSON.parse(content), callback);
        else
            authorize(JSON.parse(content), listFiles);//default
    });
}

/**
 * Create an OAuth2 client with the given credentials, and then execute the
 * given callback function.
 * @param {Object} credentials The authorization client credentials.
 * @param {function} callback The callback to call with the authorized client.
 */
function authorize(credentials, callback) {
  const {client_secret, client_id, redirect_uris} = credentials.installed;
  const oAuth2Client = new google.auth.OAuth2(
      client_id, client_secret, redirect_uris[0]);

  // Check if we have previously stored a token.
  fs.readFile(TOKEN_PATH, (err, token) => {
    if (err) return getAccessToken(oAuth2Client, callback);
    oAuth2Client.setCredentials(JSON.parse(token));
    callback(oAuth2Client);
  });
}

/**
 * Get and store new token after prompting for user authorization, and then
 * execute the given callback with the authorized OAuth2 client.
 * @param {google.auth.OAuth2} oAuth2Client The OAuth2 client to get token for.
 * @param {getEventsCallback} callback The callback for the authorized client.
 */
function getAccessToken(oAuth2Client, callback) {
  const authUrl = oAuth2Client.generateAuthUrl({
    access_type: 'offline',
    scope: SCOPES,
  });
  console.log('Authorize this app by visiting this url:', authUrl);
  const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
  });
  rl.question('Enter the code from that page here: ', (code) => {
    rl.close();
    oAuth2Client.getToken(code, (err, token) => {
      if (err) return console.error('Error retrieving access token', err);
      oAuth2Client.setCredentials(token);
      // Store the token to disk for later program executions
      fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
        if (err) return console.error(err);
        console.log('Token stored to', TOKEN_PATH);
      });
      callback(oAuth2Client);
    });
  });
}

/**
 * Lists the names and IDs of up to 10 files.
 * @param {google.auth.OAuth2} auth An authorized OAuth2 client.
 */
function listFiles(auth) {
  const drive = google.drive({version: 'v3', auth});
  drive.files.list({
    pageSize: 10,
    fields: 'nextPageToken, files(id, name)',
  }, (err, res) => {
    if (err) return console.log('The API returned an error: ' + err);
    const files = res.data.files;
    if (files.length) {
      console.log('Files:');
      files.map((file) => {
        console.log(`${file.name} (${file.id})`);
      });
    } else {
      console.log('No files found.');
    }
  });
}

callGDriveApi();

module.exports = callGDriveApi;

Basicamente, o que temos aqui é o código que implementa a autenticação OAuth offline. Ou seja, a sua aplicação vai solicitar um token para a sua conta do Google Drive, que será utilizada pela sua aplicação. Mais tarde usaremos este módulo para chamar a API do GDrive.

Ao executar esse código pela primeira vez no console (node gdrive-auth), você receberá uma URL que deve ser acessada. Copie a URL e, ao acessar a página no navegador, você verá a sua conta do Google e ela vai lhe questionar se você quer conceder acesso à essa aplicação Node (Quickstart). Fornecendo a permissão você receberá o token que deve ser informado no terminal novamente, para que seja gravado.

Se tudo ocorrer bem, um teste deve ser realizado automaticamente, listando os 10 últimos arquivos adicionados na sua conta do GDrive. Com isso, temos um projeto configurado e autenticado para acessar as APIs do Google Drive (essa autenticação é feita apenas uma vez, para obter o arquivo token.json, que fica na raiz da sua aplicação).

Atenção: o escopo da permissão que solicitamos é para gerenciar pastas e arquivos enviados pela própria aplicação. Sendo assim, no primeiro teste não será listado nenhum arquivo, pois a aplicação não criou nada ainda. Nos testes subsequentes, no console, serão listados os arquivos que já fizemos upload.

Se precisar mudar o escopo de permissão (na variável SCOPES), certifique-se também de excluir o arquivo token.json para que o processo de autenticação reinicie corretamente e ele seja criado com as permissões corretas.

Enviando uma imagem

Agora criaremos outro módulo no nosso projeto, em um arquivo gdrive.js, com o seguinte conteúdo, adaptado também da documentação oficial:

const fs = require("fs");
const {google} = require('googleapis');

function imageUpload(fileName, filePath, callback){
    require("./gdrive-auth")((auth) => {
        const fileMetadata = {
            name: fileName
        };

        const media = {
            mimeType: "image/jpeg",
            body: fs.createReadStream(filePath)
        }
        
        const drive = google.drive({version: 'v3', auth});
        drive.files.create({
            resource: fileMetadata,
            media: media,
            fields: 'id'
          }, function (err, file) {
            if (err) {
              // Handle error
              console.error(err);
            } else {
              callback(file.data.id);
            }
          });
    });
}

module.exports = { imageUpload };

Neste módulo usamos o módulo anterior, de autenticação, e depois enviamos uma imagem passada como argumento para o GDrive criar ela idêntica na nuvem.

O argumento fileName é apenas o nome da imagem, enquanto filePath é o caminho relativo ou literal. Após a criação, será executado um callback com o ID do arquivo no GDrive que acabamos de criar.

Caso queira fazer upload de outros arquivos que não sejam imagens JPG, basta trocar o mime-type.

Agora que temos um módulo de autenticação e um de upload de imagem, vamos criar um index.js para realizar alguns testes:

//index.js
const gdrive = require("./gdrive");
gdrive.imageUpload("imagem.jpg", "./imagem.jpg", (id) => {
    console.log(id);
});

Aqui estou considerando que você tem um arquivo chamado imagem.jpg dentro da pasta do projeto, no mesmo nível dos módulos.

Rode essa aplicação com “node index” e você deverá encontrar o novo arquivo na sua conta do GDrive, na raiz do mesmo.

Agora, com as adaptações certas, você pode usar esse módulo em uma web API Express ou mesmo com algum outro framework do Node, para compor uma solução de verdade.

Espero que tenha gostado!