Skip to content

Instantly share code, notes, and snippets.

@Hiyorimi
Forked from tanaikech/submit.md
Last active July 22, 2021 10:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Hiyorimi/7cb94da67d54ae808971c9fefba0f035 to your computer and use it in GitHub Desktop.
Save Hiyorimi/7cb94da67d54ae808971c9fefba0f035 to your computer and use it in GitHub Desktop.
Create Folder Tree of Google Drive using Node.js and Python

Create Folder Tree of Google Drive using Node.js

This is a sample script for retrieving a folder tree using Node.js and Python. In this sample, you can set the top of folder for the folder tree. In generally, the folder tree is created by retrieving folders from the top folder in order. For example, when Google Apps Script is used, the script becomes like this. But when Drive API is used for this situation, if there are a lot of folders in the top folder, a lot of APIs are required to be called. So in this sample, I have tried to create the folder tree by a small number of API calls as possible.

In this sample, in order to be easy to understand the flow, I used Quickstart for Node.js. When you use this sample script, at first, please check the document of Quickstart. And I confirmed that this sample worked at googleapis v30.0.0.

Flow :

  1. Retrieve all folders in Google Drive.
    • If the number of folders in your Google Drive is less than 1000, all folders are retrieved by one API call. If the number of folders is from 1000 to 2000, 2 API calls are used.
  2. Create folder tree using the list of all folders.

Output :

When the folder structure is as follows.

This script creates the folder tree as follows.

[Top-level-dir]	https://drive.google.com/drive/folders/directory_id
	[subdir1]	https://drive.google.com/drive/folders/subdir1_id
		[file1]	https://drive.google.com/file/d/file1_id
	[subdir2]	https://drive.google.com/drive/folders/subdir2_id
		[file2]	https://drive.google.com/file/d/file2_id

Sample script for getting tree structure:

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

// If modifying these scopes, delete credentials.json.
const SCOPES = ['https://www.googleapis.com/auth/drive'];
const TOKEN_PATH = 'credentials.json';

// Load client secrets from a local file.
fs.readFile('client_secret.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.
  authorize(JSON.parse(content), listFiles);
});

/**
 * 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 callback(err);
      oAuth2Client.setCredentials(token);
      // Store the token to disk for later program executions
      fs.writeFile(TOKEN_PATH, JSON.stringify(token), (err) => {
        if (err) console.error(err);
        console.log('Token stored to', TOKEN_PATH);
      });
      callback(oAuth2Client);
    });
  });
}

function listFiles(auth) {
    var drive = google.drive({version: 'v3', auth: auth});
    getFolderTree(drive, "", []);
}

function getFolderTree(drive, nextPageToken, folderList) {
    console.log('Entered getFolderTree');
    drive.files.list({
        pageToken: nextPageToken ? nextPageToken : "",
        pageSize: 1000,
        //q: "mimeType='application/vnd.google-apps.folder'",
        fields: "files(id,name,parents),nextPageToken",
    }, (err, result) => {
        if (typeof result !== 'undefined') {
        var data = result.data;
//        console.log(data);
        if (err) return console.log('The API returned an error: ' + err);
        const token = data.nextPageToken;
        Array.prototype.push.apply(folderList, data.files);
        if (token) {
            getFolderTree(drive, token, folderList);
        } else {
            console.log('Token not Found');
            // This script retrieves a folder tree under this folder ID.
            const folderId = "someFolderId";

            const folderTree = function c(folder, folderSt, res) {
                let ar = folderList.filter(e => e.parents[0] == folder);
                folderSt += folder + "#_aabbccddee_#";
                let arrayFolderSt = folderSt.split("#_aabbccddee_#");
                arrayFolderSt.pop();
                res.push(arrayFolderSt);
                ar.length == 0 && (folderSt = "");
                ar.forEach(e => c(e.id, folderSt, res));
                return res;
            }(folderId, "", []);

            // Output the folder tree.
            // Doesn't work for some reasons.
            console.log('Outputing folder tree:');
            console.log(folderTree);
        }
        }
        else {
            console.log('Result is undefined');
            var fs = require('fs');
            fs.writeFile ("input_children.json", JSON.stringify(folderList), function(err) {
                if (err) throw err;
                console.log('complete');
                }
            );
        }
    });
}

Sample script for printing tree structure:

#!/usr/bin/env python

import json

FOLDER_URL_TEMPLATE = 'https://drive.google.com/drive/folders/{folder_id}'
FILE_URL_TEMPLATE = 'https://drive.google.com/file/d/{file_id}/view'
    

with open('input_children.json', 'r') as fp:
    gdrive_tree_structure = json.loads(fp.read())

tree = open('tree.txt', 'w')

tree_dict = { node['id']: node for node in gdrive_tree_structure }
for node in gdrive_tree_structure:
    if 'parents' in node and node['parents']:
        parent_id = node['parents'][0]
        try:
            tree_dict[parent_id]['has_children'] = True
        except KeyError:
            tree_dict[parent_id] = {}
            tree_dict[parent_id]['has_children'] = True

def get_url(node: dict) -> str:
    """Builds URL to file or folder depending on the node type."""
    if 'has_children' in tree_dict[node['id']]:
        return FOLDER_URL_TEMPLATE.format(folder_id=node['id'])
    return FILE_URL_TEMPLATE.format(file_id=node['id'])

def print_children(folder_id: str, level: int):
    """Prints children of the directory defined by folder_id."""
    top_level = list(
        filter(
            lambda x: x['parents'] == [folder_id] if 'parents' in x else False,
            gdrive_tree_structure,
        ))
    for node in top_level:
        tree.write('\t' * level + 
        node['name'] +
        '\t' +
        get_url(node) +
        '\n')
        print_children(node['id'], level + 1)


print_children('secret_folder_id', 0)

tree.close()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment