Google drive file picker in meteor js

Hello, Can anyone please help me with code/ information about implementation of Google Drive File Picker in Meteorjs.

I have gone through the google docs and there they have provided Javascript code but am unable to use it in my meteor web application.
Please help me out on this.

@naveenmergerware Please explain the following

  • What you have tried
  • What errors are you getting?
  • Where do you think the problem is?
  • Are you using React or Blaze? (or something else)

and most importantly, post your code so we can help you

Hi @mikkelking thank you for the reply.

I have tried Google drive file picker in JavaScript and it works but I want to implement it in Meteor with Blaze.

Below is JavaScript code.

<!DOCTYPE html>
<html>
<head>
  <title>Picker API Quickstart</title>
  <meta charset="utf-8" />
</head>
<body>
<p>Picker API API Quickstart</p>

<!--Add buttons to initiate auth sequence and sign out-->
<button id="authorize_button" onclick="handleAuthClick()">Authorize</button>
<button id="signout_button" onclick="handleSignoutClick()">Sign Out</button>

<pre id="content" style="white-space: pre-wrap;"></pre>

<script type="text/javascript">

  /* exported gapiLoaded */
  /* exported gisLoaded */
  /* exported handleAuthClick */
  /* exported handleSignoutClick */

  // Authorization scopes required by the API; multiple scopes can be
  // included, separated by spaces.
  const SCOPES = 'https://www.googleapis.com/auth/drive.metadata.readonly';

  // TODO(developer): Set to client ID and API key from the Developer Console
  const CLIENT_ID = "3242*****************.apps.googleusercontent.com";
  const API_KEY = 'A***********************************PJs';

  // TODO(developer): Replace with your own project number from console.developers.google.com.
  const APP_ID = '32*****************';

  let tokenClient;
  let accessToken = null;
  let pickerInited = false;
  let gisInited = false;


  document.getElementById('authorize_button').style.visibility = 'hidden';
  document.getElementById('signout_button').style.visibility = 'hidden';

  /**
   * Callback after api.js is loaded.
   */
  function gapiLoaded() {
    gapi.load('client:picker', intializePicker);
  }

  /**
   * Callback after the API client is loaded. Loads the
   * discovery doc to initialize the API.
  */
  async function intializePicker() {
    await gapi.client.load('https://www.googleapis.com/discovery/v1/apis/drive/v3/rest');
    pickerInited = true;
    maybeEnableButtons();
  }

  /**
   * Callback after Google Identity Services are loaded.
  */
  function gisLoaded() {
    tokenClient = google.accounts.oauth2.initTokenClient({
      client_id: CLIENT_ID,
      scope: SCOPES,
      callback: '', // defined later
    });
    gisInited = true;
    maybeEnableButtons();
  }

  /**
   * Enables user interaction after all libraries are loaded.
   */
  function maybeEnableButtons() {
    if (pickerInited && gisInited) {
      document.getElementById('authorize_button').style.visibility = 'visible';
    }
  }

  /**
   *  Sign in the user upon button click.
   */
  function handleAuthClick() {
    tokenClient.callback = async (response) => {
      if (response.error !== undefined) {
        throw (response);
      }
      accessToken = response.access_token;
      document.getElementById('signout_button').style.visibility = 'visible';
      document.getElementById('authorize_button').innerText = 'Refresh';
      await createPicker();
    };

    if (accessToken === null) {
      // Prompt the user to select a Google Account and ask for consent to share their data
      // when establishing a new session.
      tokenClient.requestAccessToken({prompt: 'consent'});
    } else {
      // Skip display of account chooser and consent dialog for an existing session.
      tokenClient.requestAccessToken({prompt: ''});
    }
  }

  /**
   *  Sign out the user upon button click.
   */
  function handleSignoutClick() {
    if (accessToken) {
      accessToken = null;
      google.accounts.oauth2.revoke(accessToken);
      document.getElementById('content').innerText = '';
      document.getElementById('authorize_button').innerText = 'Authorize';
      document.getElementById('signout_button').style.visibility = 'hidden';
    }
  }

  /**
   *  Create and render a Picker object for searching images.
   */
  function createPicker() {
    const view = new google.picker.View(google.picker.ViewId.DOCS);
    // view.setMimeTypes('image/png,image/jpeg,image/jpg');
    const picker = new google.picker.PickerBuilder()
        // .enableFeature(google.picker.Feature.NAV_HIDDEN)
        .enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
        .setDeveloperKey(API_KEY)
        .setAppId(APP_ID)
        .setOAuthToken(accessToken)
        .addView(view)
        .addView(new google.picker.DocsUploadView())
        .setCallback(pickerCallback)
        .build();
    picker.setVisible(true);
  }

  /**
   * Displays the file details of the user's selection.
   * @param {object} data - Containers the user selection from the picker
   */
  async function pickerCallback(data) {
    if (data.action === google.picker.Action.PICKED) {
      let text = `Picker response: \n${JSON.stringify(data, null, 2)}\n`;
      const document = data[google.picker.Response.DOCUMENTS][0];
      const fileId = document[google.picker.Document.ID];
      const res = await gapi.client.drive.files.get({
          'fileId': fileId,
          'fields': '*',
      });

    const dat =  getFile(fileId)
    dat.then(function(result) {
      console.log(result) // "Some User token"
    })
  };
}
//Download the file to blob format
  async function getFile (fileId) {
    const URL = 'https://www.googleapis.com/drive/v3/files';
    const FIELDS = 'name, mimeType, modifiedTime';
    
    const { gapi: { auth, client: { drive: { files } } } } = window;
    const { access_token: accessToken } = auth.getToken();
    const fetchOptions = { headers: { Authorization: `Bearer ${accessToken}` } };
    
    const {
      result: { name, mimeType, modifiedTime }
    } = await files.get({ fileId, fields: FIELDS });
    const blob = await fetch(`${URL}/${fileId}?alt=media`, fetchOptions).then(res => res.blob());
    const fileOptions = {
      type: mimeType,
      lastModified: new Date(modifiedTime).getTime(),
    };
    
  return new File([blob], name, fileOptions);
};
</script>
<script async defer src="https://apis.google.com/js/api.js" onload="gapiLoaded()"></script>
<script async defer src="https://accounts.google.com/gsi/client" onload="gisLoaded()"></script>
</body>
</html>
  • Where do you think the problem is?
    I want to implement this in Meteor with Blaze but I am unable to get any documentation to implement it in Meteor.

This doesn’t look like Meteor code. I think you might need to do the Meteor Blaze tutorial to understand how to do it

HI @mikkelking, As I posted a question regarding google drive file picker, as of now google drive file picker is opening but when select any file to download am getting an error.
Below am posting my code.

Template.vdrEntry.onRendered(function () {

  $.getScript("https://apis.google.com/js/api.js",async function(){
    gapiLoaded()
  });

  $.getScript("https://accounts.google.com/gsi/client",function(){
    gisLoaded()
  });
})

  async function gapiLoaded(){
    gapi.load('client:picker', initializePicker);
  }; 
  
//Note: I think gapi.client.load is not working
  async function initializePicker() {
    await gapi.client.load('https://www.googleapis.com/discovery/v1/apis/drive/v3/rest');
    pickerInited = true;
  }

Template.vdrEntry.events({
    //Here I have used Meteor.loginwithgoogle package to authenticate the user and get the access token.

  'click #authorize_button'(e, template){
    // @ts-ignore
     Meteor.loginWithGoogle({
        requestPermissions: ['email', 'https://www.googleapis.com/auth/drive.file'],
        forceApprovalPrompt: true,
        requestOfflineToken: true,
    }, async (err) => {
      if (err) {
        console.log('err')
        createPicker()
      } else {
        console.log('success')
      }
    });
  },
})

/**
 *  Create and render a Picker object for searching images.
*/
function createPicker() {
  
  const view = new google.picker.View(google.picker.ViewId.DOCS);
  // view.setMimeTypes('image/png,image/jpeg,image/jpg');
  const picker = new google.picker.PickerBuilder()
      // .enableFeature(google.picker.Feature.NAV_HIDDEN)
      .enableFeature(google.picker.Feature.MULTISELECT_ENABLED)
      .setDeveloperKey(API_KEY)
      .setAppId(APP_ID)
      .setOAuthToken(accessToken)
      .addView(view)
      .addView(new google.picker.DocsUploadView())
      .setCallback(pickerCallback)
      .build();
  picker.setVisible(true);
}

async function pickerCallback(data) {

  if (data.action === google.picker.Action.PICKED) {
    let text = `Picker response: \n${JSON.stringify(data, null, 2)}\n`;
    const document = data[google.picker.Response.DOCUMENTS][0];
    const fileId = document[google.picker.Document.ID];
//Till here it is working I am getting selected fileId but when I try to download it is throwing error

//This is not working when try to get the file
    const res = await gapi.client.drive.files.get({
      'fileId': fileId,
      'fields': '*',
    });
}



Error getting for drive.files.get function

Please let me know what am missing and is that ok to use Meteor.loginwithgoogle after user logg-In into the application using email and OTP.

Meteor.loginWithGoogle after using another login service will most likely create two different accounts. Have a look at this package : https://atmospherejs.com/bozhao/link-accounts to see about linking social media accounts to email accounts.