前言
随着谷歌表格API升级到第四个版本以后,虽然提供了更快的表格数据读取API,但是还有就是需要授权才能读取到数据,即使是发布到网络的表格。所以我们在做这一升级的时候,尝试了很多的方法,也走过了很多的坑,下面我就简单总结一下。
方案
首先我找到了读取表格数据的API 接口:
developers.google.com/sheets/api/…
该接口需要的参数如下(必传的参数):
{
"spreadsheetId": "",
"range": "",
"access_token": ""
}
使用过的朋友都知道,spreadsheetId和range是表格的id和表格的tab范围。但是access_token
并不是一个固定的东西,所以我研究了google获取access_token
的两种方式(都是基于OAuth 2.0的):
- 使用用户自己的账号,简单来讲就是通知用户授权,然后用户确认后可以拿到用户的
access_token
读取该用户拥有读取权限的表格数据。 - 使用服务账号,将表格共享给这个谷歌账号,然后使用服务器登录服务账号,获取服务账号的
access_token
读取表格数据。
刚开始的时候我使用了第一种方式,但是出现了下面的缺点:
- 需要写死一个账号,每次都是用这个账号进行授权。
access_token
的有效期很短(一般是两个小时),隔一段时间需要手动授权,需要人工的干预。- 要是哪天我的账号被删除了就需要重新设置新的账号,并生成新的项目配置。
后来看到谷歌在服务器授权的时候,建议使用服务账号(虽然API KEY也可以),就实现了服务端无用户参与的授权。
实现
一、创建一个项目
在开发者控制台创建一个项目
link:console.developers.google.com/projectcrea…
填写完必要的信息后,直接点击创建即可(注意项目的id不能修改)。
二、开通Google Sheet API服务
在创建完账号以后就会跳转到该项目的dashboard,如下面的视图,直接在搜索框搜索Google Sheet API,如果不开启创建的服务账号是没有权限读取表格数据的。
在API页面直接点击启用即可开启该项目的Sheet API功能。
三、生成一个服务账号
开启Sheet API以后会跳转到Sheet API的配置页面,我们现在为这个项目创建一个服务账号。
首先按照标记点击"凭据",然后点击"API与服务中的凭据"来创建服务账号。
我们在这里选择创建"服务账号密钥";
四、生成凭据
点击上面的"服务账号密钥"跳转到了密钥创建页面,我们选择新的服务账号,根据自己的要求进行填写,如下所示。
这里我选择了JSON格式,方便Node.js(本次以Node.js作为实践)读取;
点击创建以后我们会得到一个json文件,注意改json文件一定要保存好了,后面的授权全靠它了。
接下来就开始编码实现读取的功能了;
五、开始编码
下面是Node.js作为实现,首先需要安装googleapis,下面代码是授权部分。
npm install googleapis --save
const {google} = require('googleapis');
// 后面的json文件就是我们在上面第四步下载的json文件。
const {client_email: email, private_key: key} = require('./service-account.json');
// 指定权限,这里仅需要读取权限就可以了
const scopes = ['https://www.googleapis.com/auth/spreadsheets.readonly'];
// 全局存储授权后的token信息,后面也可以存储到redis等缓存中,现在为了测试,我就存储在本地的变量中
let token = null;
module.exports = {
authorize() {
return new Promise((resolve) => {
// 首先判断有没有token文件
if (token) {
const {access_token, expiry_date} = token;
// 看看有没有过期 + 1分钟
if (expiry_date > (new Date().getTime() + 60 * 1000)) {
return resolve(access_token);
}
}
// 重新授权
let jwtClient = new google.auth.JWT(email, null, key, scopes);
// 直接可以获取到access_token,很方便,不需要人工干预
jwtClient.authorize().then(data => {
// 保存token到全局变量
token = data;
// 返回token
resolve(data.access_token)
}).catch(() => {
resolve('');
});
});
}
};
然后我们通过上面的authorize可以取到token信息,最后使用ajax客户端来调用表格的接口,即可实现数据的读取。
下面是读取表格数据的实现:
module.exports = async function getSheetData({spreadsheetId, worksheets}) {
const axios = require('axios');
const qs = require('qs');
const auth = require('../auth/jwtclient');
const serviceEmailAddr = require('../auth/service-account').client_email;
const token = await auth.authorize();
const url = `https://sheets.googleapis.com/v4/spreadsheets/${spreadsheetId}/values:batchGet`;
return await axios
.get(url, {
params: {access_token: token, ranges: worksheets},
paramsSerializer(params) {
return qs.stringify(params, {arrayFormat: 'repeat', encodeValuesOnly: true});
}
})
.then(resp => resp.data.valueRanges)
.catch(error => {
if (error.response.status === 404) {
return Promise.reject('表格不存在,请检查表格id是否正确');
}
if (error.response.status === 403) {
return Promise.reject('没有权限访问,请将表格分享给:' + serviceEmailAddr);
}
if (error.response.status === 400) {
return Promise.reject(error.response.data.error.message.replace('Unable to parse range', '请检查后面表格是否存在'));
}
Promise.reject(error.message);
});
};
为了保证隐私,我没有上传项目配置的json文件;
六、测试
如果读取数据的时候,google返回了403,那可能是因为你没有将表格共享给服务账号,查看服务账号:
console.developers.google.com/iam-admin/s…
在上面选择你的项目,就能看到右边的电子邮件,将谷歌表格共享给改邮箱就可以访问了。