最近在工作中遇到了一个场景:要做一个静态的,里面的内容是由编写的.md格式的内容。设计将编好的文档统一放在常用的 Drive里面,如下图

Nodejs读取Google Drive里面的文件

 

然后我需要将这些文档下载下来导入到我的项目里面,然后进行解析编译,最后展示在网页上面。最开始,每次下载.md、删除项目里面之前的.md、再导入新的.md, 没什么问题,但是设计时不时更新一下文档,然后我每次我都要痛苦地执行上面的操作,关键是这些.md还不止一个文件夹,文档的数量也有近百个,重复工作不胜其烦。

为了解决这个问题,我想到使用NodeJS编写一个简单的脚本,直接读取Google Drive里面的文件。下面记录了一些过程

Google搜索google drive api

 

非常多的API, 直接找我们需要的Download

 

然后这里有官方提供的案例

 

copy到项目里面,在build文件夹下建一个test.js,将刚刚copy的脚本放在里面。但是这个脚本,我们缺少fileId,拿不到fileId就无法去下载。于是去找get方法

 

从list返回集里面可以找到fileId, 且参数里面传入我们的driveId(文件夹的ID)

 

所以,我们现在我们依赖两个API: list和get。我们先读取文件夹里面全部的文件

async function list(folderId, limit = 100) {
  let files = [];
  let pageToken = null;
  let listOptions = {
    q: `'${folderId}' in parents`,
    fields: 'files(id,name,mimeType,trashed)'
  };
  while (true) {
    if (pageToken) {
      listOptions['pageToken'] = pageToken;
    }
    let response = await drive.files.list(listOptions);
    if (!response.data.files || !response.data.files.length) {
      break;
    }
    let limitReached = false;
    for (let file of response.data.files) {
      if (file.trashed) {
        continue;
      }
      delete (file.trashed);
      files.push(file);
      if (files.length >= limit) {
        limitReached = true;
        break;
      }
    }
    pageToken = response.data.nextPageToken;
    if (limitReached || !pageToken) {
      break;
    }
  }
  return files;
}

期望返回的files如下:

[
  {
    id: '3434UTHhlvdvpFL4hdHjsxImiLPKYLYh6VpK',
    name: 'Default Page.md',
    mimeType: 'text/markdown'
  },
  {
    id: '134Za--w6fKhGSZGPc7vWAn_ejg88Sx4pqf',
    name: 'Anchor.md',
    mimeType: 'text/markdown'
  },
]

这里的id就是fileId, 然后我们就可以通过

files.forEach(file => {
  drive.files.get({ fileId: file.id, alt: 'media' }).then(res => {
		console.log('res', res.data)
    // writeFile
  });
});

到这里,基本上主要的Google Drive的API已经用完了,但是,我们知道使用Google Drive是需要登录认证,只有授权账户才能访问文件。所以接下来的工作就是获取Google Auth。

 

Get Google Auth

 

从Google API Console[
https://console.cloud.google.com/apis/dashboard]进入Google Developer Console。新建一个project

 

填写project信息

 

这样,project就创建好了,我们还需要给它配置api

 

选择google drive

 

启用google drive

 

为了使用API, 我们创建凭据

 

继续

 

继续

 

继续

 

继续

 

完成后就看到生成的服务账号了,点击编辑

 

生成秘钥

 

导出为json格式,并保存到项目中credentials.json

 

接下来,我们就可以使用生成的凭据来获得Google Auth了

const { google } = require("googleapis");
const credentials = require("./credentials.json");
const scopes = ["https://www.googleapis.com/auth/drive"];
const auth = new google.auth.JWT(
  credentials.client_email,
  null,
  credentials.private_key,
  scopes
);
const drive = google.drive({ version: "v3", auth });

至此,我们的读取Google Drive的功能全部介绍到了。但是想要使用,还需要给对象文件(夹)share with service account email id.

 

这里的email来源于上面credentials.json里面的的client_email。

 

至此,我们就能真正访问到Google Drive里面的文件了。下面是全部的脚本

const { google } = require("googleapis");
const writeFile = require('write');
const { resolve } = require('path');

const folders = [
  {
    name: '组件文档',
    folderId: '1sfsCs5ojo4g-RJU-GamENaetNpJlpVYiNQ',
    dest: 'pages/components'
  },
  {
    name: '组件日志',
    folderId: '1MsdVAyt7_c1qsXM8ZxyVV_lxXG2_VhD6fW',
    dest: 'pages/record'
  },
  {
    name: '设计文档',
    folderId: '12IkZBRsdNjW_D0C3pKOehkGv0uZO3',
    dest: 'pages/design'
  },
]

const credentials = require("./credentials.json");
const scopes = ["https://www.googleapis.com/auth/drive"];
const auth = new google.auth.JWT(
  credentials.client_email,
  null,
  credentials.private_key,
  scopes
);
const drive = google.drive({ version: "v3", auth });

// 读取文件夹里面的文件,返回的数据格式如下:
// {
//   id: '1me2MPIehUw9LeP7sj4Px-V4_m6mIGFZ',
//   name: 'Button.md',
//   mimeType: 'text/markdown'
// }
async function list(folderId, limit = 100) {
  let files = [];
  let pageToken = null;
  let listOptions = {
    q: `'${folderId}' in parents`,
    fields: 'files(id,name,mimeType, trashed)'
  };
  while (true) {
    if (pageToken) {
      listOptions['pageToken'] = pageToken;
    }
    let response = await drive.files.list(listOptions);
    if (!response.data.files || !response.data.files.length) {
      break;
    }
    let limitReached = false;
    for (let file of response.data.files) {
      if (file.trashed) {
        continue;
      }
      delete (file.trashed);
      files.push(file);
      if (files.length >= limit) {
        limitReached = true;
        break;
      }
    }
    pageToken = response.data.nextPageToken;
    if (limitReached || !pageToken) {
      break;
    }
  }
  return files;
}

folders.forEach(folder => {
  const { folderId, dest } = folder;
  list(folderId).then(files => {
    // console.log('files', files);
    files.forEach(file => {
      drive.files.get({ fileId: file.id, alt: 'media' }).then(res => {
        writeFile(`${resolve(dest)}/${file.name}`, res.data, {encoding: 'utf-8'});
        console.log(`Download ${resolve(dest)}/${file.name} success`);
      });
    });
  });
});

最终,完成需求,解放双手

胜象大百科