手把手教学用nodejs读写飞书在线表格

背景

1.业务需求,需要读取飞书的数据整理后打包成excel发给运营。

2.表格背景:在同事的知识库中的在线表格

3.参考:飞书api文档-写入:open.feishu.cn/document/se...

  1. 技术栈:nestjs

全程大概半个小时搞定,大家可以在看下飞书的api文档。 同时也练下全栈技术

好了,废话不多说,下边进入正题,放入核心代码,dto啥的可忽略

读取飞书表格数据

js 复制代码
import { Injectable } from '@nestjs/common';
import axios from 'axios';
import { defaultPlatforms, formatError, formatPlatformName, formatSuccess, getExcelCellText } from 'src/util';
import { WhiteExcelDto } from './dto/whiteExcel.dto';

const feiShuUrl = '<https://mcndvais4o02.feishu.cn/wiki/Fp9ZwDLTsi2kSxkP40jc536YnWe>'; // 飞书表格链接'
@Injectable()
export class FeiShuExcelService {
private readonly appId = '你的id';
private readonly appSecret = '你的appSecret';
private accessToken: string;
private tokenExpireTime: number;

async getExcel({ url, platforms, columns }: { url: string; platforms?: string; columns?: string }) {
const \_url = url || `${feiShuUrl}?sheet=IDmK34`; // 飞书表格链接
const wikiRes: any = await this.extractWikiTokens(\_url);

    // 查知识库
    const zskRes = await this.getWikiNodes(wikiRes.spaceId);

    // 查表格
    const sheet = _url.split('?sheet=')[1];
    const inputColumns = (columns || '').trim().split(',').filter(Boolean);
    const columnNames = inputColumns?.length ? inputColumns : ['创建时间', '监测时间', '短剧BID', '剧单信息-短剧名', '平台', '侵权链接', '账号id', '账号名'];
    const excelRes = await this.getSheetInfo(zskRes?.obj_token, sheet, platforms, columnNames);

    return formatSuccess(excelRes);

}

// 获取表格元数据
async getSheetInfo(spreadsheetToken: string, sheetId?: string, platforms = defaultPlatforms, columns?: string\[]) {
const token = await this.getAccessToken();
// 示例值:"Q7PlXT!A1:B2" 不加范围是整个表数据
const url = `https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/${spreadsheetToken}/values/${sheetId}`;
const headers = { Authorization: `Bearer ${token}` };
const response: any = await axios.get(url, { headers });
const datas = response?.data?.data?.valueRange?.values || \[];
// console.log('==== response?.data: ', response?.data);
const theader = datas\[0].map(getExcelCellText);
const tbody = datas.slice(1);
const res: any = {
len: tbody?.length,
list: tbody,
};
const selectedColumnsIndexes = \[];
theader.forEach((item: any, index: number) => {
if (columns?.length) {
if (columns?.includes(item)) {
selectedColumnsIndexes.push(index);
}
} else {
selectedColumnsIndexes.push(index);
}
});
const platformList = platforms.split(',');
const platformIndex = theader.findIndex((item: any) => item === '平台');
// console.log('datas', datas.length, platformList);
const list = datas
.filter((item: any, i) => (platformList.length ? platformList.includes(formatPlatformName(item\[platformIndex])) : true))
.map((item: any) => selectedColumnsIndexes.reduce((prev, next) => ({ ...prev, \[theader\[next]]: getExcelCellText(item\[next]) }), {}));
res.list = list;
res.len = list.length;
return res;
}

// 获取访问令牌
private async getAccessToken(): Promise<any> {
if (this.accessToken && Date.now() < this.tokenExpireTime) {
return this.accessToken;
}
const response: any = (
await axios.post('<https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal>', {
app\_id: this.appId,
app\_secret: this.appSecret,
})
)?.data;
console.log('==获取访问令牌-response: ', response.data);
if (response.code !== 0) return false;
this.accessToken = response.tenant\_access\_token;
this.tokenExpireTime = Date.now() + (response.expire - 60) \* 1000; // 提前60秒刷新
return this.accessToken;
}

// 从知识库URL中提取知识库token和节点token
private extractWikiTokens(url: string): { spaceId: string; nodeToken?: string } {
const spaceMatch = url.match(/wiki/(\[a-zA-Z0-9]+)/);
const nodeMatch = url.match(/\[?&]sheet=(\[a-zA-Z0-9]+)/);
return {
spaceId: spaceMatch ? spaceMatch\[1] : null,
nodeToken: nodeMatch ? nodeMatch\[1] : null,
};
}

// 获取知识库空间下的所有节点
async getWikiNodes(wikeToken: string) {
const token = await this.getAccessToken();
try {
const response = await axios.get(`https://open.feishu.cn/open-apis/wiki/v2/spaces/get_node`, {
params: { obj\_type: 'wiki', token: wikeToken },
headers: { Authorization: `Bearer ${token}` },
});
// console.log('response.data: ', response.data);
if (response.data.code !== 0) {
return `获取知识库节点失败: ${response.data.msg}`;
}
const { obj\_type, obj\_token } = response?.data?.data?.node;
return { obj\_type, obj\_token };
} catch (error) {
console.log('---- error: ', error);
return error;
}
}
}

在飞书表格中写入数据

在上述的基础上增加如下代码即可

js 复制代码
// 从知识库URL中提取知识库token和节点token
private extractWikiTokens(url: string): { spaceId: string; nodeToken?: string } {
const spaceMatch = url.match(/wiki/(\[a-zA-Z0-9]+)/);
const nodeMatch = url.match(/\[?&]sheet=(\[a-zA-Z0-9]+)/);
return {
spaceId: spaceMatch ? spaceMatch\[1] : null,
nodeToken: nodeMatch ? nodeMatch\[1] : null,
};
}

// 获取知识库空间下的所有节点
async getWikiNodes(wikeToken: string) {
const token = await this.getAccessToken();
try {
const response = await axios.get(`https://open.feishu.cn/open-apis/wiki/v2/spaces/get_node`, {
params: { obj\_type: 'wiki', token: wikeToken },
headers: { Authorization: `Bearer ${token}` },
});
// console.log('response.data: ', response.data);
if (response.data.code !== 0) {
return `获取知识库节点失败: ${response.data.msg}`;
}
const { obj\_type, obj\_token } = response?.data?.data?.node;
return { obj\_type, obj\_token };
} catch (error) {
console.log('---- error: ', error);
return error;
}
}

至此大工告成,看下写入的效果 如果有帮到你请关注点赞哈,为我创作增加动力

相关推荐
wuk9987 小时前
实现ROS系统的Websocket传输,向Web应用推送sensor_msgs::Image数据
前端·websocket·网络协议
合作小小程序员小小店8 小时前
web网页开发,在线%考试管理%系统,基于Idea,vscode,html,css,vue,java,maven,springboot,mysql
java·前端·系统架构·vue·intellij-idea·springboot
天天进步20159 小时前
CSS Grid与Flexbox:2025年响应式布局终极指南
前端·css
Boop_wu10 小时前
[Java EE] 计算机基础
java·服务器·前端
Novlan110 小时前
TDesign UniApp 组件库来了
前端
用户479492835691510 小时前
React DevTools 组件名乱码?揭秘从开发到生产的代码变形记
前端·react.js
顾安r11 小时前
11.8 脚本网页 打砖块max
服务器·前端·html·css3
倚栏听风雨11 小时前
typescript 方法前面加* 是什么意思
前端
狮子不白11 小时前
C#WEB 防重复提交控制
开发语言·前端·程序人生·c#
菜鸟‍11 小时前
【前端学习】阿里前端面试题
前端·javascript·学习