目录

[Go] 飞书电子表格导出Excel文件

目录

飞书电子表格网页版有导出Excel功能,但公布的API文档中没有此接口,目前使用读取单元格数据, 然后使用excelize流式写入excel,间接实现导出功能。demo如下

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"

	"github.com/xuri/excelize/v2"
)

type RespToken struct {
	Code              int    `json:"code"`
	Expire            int    `json:"expire"`
	Msg               string `json:"msg"`
	TenantAccessToken string `json:"tenant_access_token"`
}

type RespSpreadsheets struct {
	Code int `json:"code"`
	Data struct {
		Revision         int    `json:"revision"`
		Spreadsheettoken string `json:"spreadsheetToken"`
		Valuerange       struct {
			Majordimension string          `json:"majorDimension"`
			Range          string          `json:"range"`
			Revision       int             `json:"revision"`
			Values         [][]interface{} `json:"values"`
		} `json:"valueRange"`
	} `json:"data"`
	Msg string `json:"msg"`
}

// 获取 tenant_access_token(企业自建应用)
// https://open.feishu.cn/document/ukTMukTMukTM/ukDNz4SO0MjL5QzM/auth-v3/auth/tenant_access_token_internal
func get_tenant_access_token(app_id, app_secret string) (token string) {
	url := "https://open.feishu.cn/open-apis/auth/v3/tenant_access_token/internal"

	payload := map[string]string{
		"app_id":     app_id,
		"app_secret": app_secret,
	}

	bytesData, _ := json.Marshal(payload)

	request, err := http.NewRequest("POST", url, bytes.NewBuffer(bytesData))
	if err != nil {
		panic(err)
	}

	request.Header.Set("Content-Type", "application/json; charset=utf-8")

	client := http.Client{}
	resp, err := client.Do(request)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	statusCode := resp.StatusCode
	if statusCode == 200 {
		body, _ := ioutil.ReadAll(resp.Body)

		var Resp RespToken
		json.Unmarshal(body, &Resp)

		switch Resp.Code {
		case 0:
			token = Resp.TenantAccessToken
		}
	}

	return token
}

// 读取单元格
// https://open.feishu.cn/document/ukTMukTMukTM/ugTMzUjL4EzM14COxMTN
func getSpreadsheets(token, spreadsheetToken, sheetId string) {
	url := "https://open.feishu.cn/open-apis/sheets/v2/spreadsheets/" + spreadsheetToken + "/values/" + sheetId
	request, err := http.NewRequest("GET", url, nil)
	if err != nil {
		panic(err)
	}

	request.Header.Set("Content-Type", "application/json; charset=utf-8")
	request.Header.Set("Authorization", "Bearer "+token)

	client := http.Client{}
	resp, err := client.Do(request)
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()

	statusCode := resp.StatusCode
	if statusCode == 200 {
		body, _ := ioutil.ReadAll(resp.Body)

		var Resp RespSpreadsheets
		json.Unmarshal(body, &Resp)
		values := Resp.Data.Valuerange.Values

		newExcel := excelize.NewFile()
		newExcel.Path = "./" + sheetId + ".xlsx"

		sheetName := newExcel.GetSheetName(newExcel.GetActiveSheetIndex())
		streamSheet, err := newExcel.NewStreamWriter(sheetName)
		if err != nil {
			panic(err)
		}

		for i, v := range values {
			// i从0开始,而excel是从第1行开始
			streamSheet.SetRow(fmt.Sprintf("A%d", i+1), v)
		}

		// 刷新至文件,此步骤不能缺
		if err := streamSheet.Flush(); err != nil {
			fmt.Println(err)
		}

		if err := newExcel.Save(); err != nil {
			fmt.Println(err)
		}
	}
}

func main() {
	app_id := "飞书应用app_id"
	app_secret := "飞书应用app_secret"
	spreadsheetToken := "飞书电子表格Token"
	sheetId := "飞书表格id"

	token := get_tenant_access_token(app_id, app_secret)
	getSpreadsheets(token, spreadsheetToken, sheetId)
}