最近在工作中遇到了一個(gè)場(chǎng)景:要做一個(gè)靜態(tài)的網(wǎng)站,里面的內(nèi)容是由設(shè)計(jì)編寫的.md格式的內(nèi)容。設(shè)計(jì)將編好的文檔統(tǒng)一放在常用的google Drive里面,如下圖

然后我需要將這些文檔下載下來(lái)導(dǎo)入到我的項(xiàng)目里面,然后進(jìn)行解析編譯,最后展示在網(wǎng)頁(yè)上面。最開(kāi)始,每次下載.md、刪除項(xiàng)目里面之前的.md、再導(dǎo)入新的.md, 沒(méi)什么問(wèn)題,但是設(shè)計(jì)時(shí)不時(shí)更新一下文檔,然后我每次我都要痛苦地執(zhí)行上面的操作,關(guān)鍵是這些.md還不止一個(gè)文件夾,文檔的數(shù)量也有近百個(gè),重復(fù)工作不勝其煩。
為了解決這個(gè)問(wèn)題,我想到使用NodeJS編寫一個(gè)簡(jiǎn)單的腳本,直接讀取Google Drive里面的文件。下面記錄了一些過(guò)程
Google搜索google drive api

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

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

copy到項(xiàng)目里面,在build文件夾下建一個(gè)test.js,將剛剛copy的腳本放在里面。但是這個(gè)腳本,我們?nèi)鄙賔ileId,拿不到fileId就無(wú)法去下載。于是去找get方法

從list返回集里面可以找到fileId, 且參數(shù)里面?zhèn)魅胛覀兊膁riveId(文件夾的ID)

所以,我們現(xiàn)在我們依賴兩個(gè)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, 然后我們就可以通過(guò)
files.forEach(file => {
drive.files.get({ fileId: file.id, alt: 'media' }).then(res => {
console.log('res', res.data)
// writeFile
});
});
到這里,基本上主要的Google Drive的API已經(jīng)用完了,但是,我們知道使用Google Drive是需要登錄認(rèn)證,只有授權(quán)賬戶才能訪問(wèn)文件。所以接下來(lái)的工作就是獲取Google Auth。
Get Google Auth

從Google API Console[
https://console.cloud.google.com/apis/dashboard]進(jìn)入Google Developer Console。新建一個(gè)project

填寫project信息

這樣,project就創(chuàng)建好了,我們還需要給它配置api

選擇google drive

啟用google drive

為了使用API, 我們創(chuàng)建憑據(jù)

繼續(xù)

繼續(xù)

繼續(xù)

繼續(xù)

完成后就看到生成的服務(wù)賬號(hào)了,點(diǎn)擊編輯

生成秘鑰

導(dǎo)出為json格式,并保存到項(xiàng)目中credentials.json

接下來(lái),我們就可以使用生成的憑據(jù)來(lái)獲得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的功能全部介紹到了。但是想要使用,還需要給對(duì)象文件(夾)share with service account email id.

這里的email來(lái)源于上面credentials.json里面的的client_email。

至此,我們就能真正訪問(wèn)到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: '設(shè)計(jì)文檔',
folderId: '12IkZBRsdNjW_D0C3pKOseoehkGv0uZO3',
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 });
// 讀取文件夾里面的文件,返回的數(shù)據(jù)格式如下:
// {
// id: '1me2MPIehUw9LeP7sj4Px-V45G_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`);
});
});
});
});
最終,完成需求,解放雙手