场景:公司各个同事的飞书空间云文档需要批量导出成本地文件,飞书不能直接批量导出(2025-01-17),但是提供了导出单个云文档的sdk。
解决思路:
飞书开发者平台,使用UserAccessToken去获取用户 “我的空间” 根目录,然后进行遍历递归导出。
这里没有使用AppToken的原因是因为AppToken拿不到其他人的空间根目录,UserAccessToken获取的方式是通过用户使用Oauth授权。要自己写一个授权html页面,然后用户在页面点击按钮跳转到飞书授权界面,用户同意授权之后,你的后台会拿到这个用户的UserAccessToken,具体方法可以看飞书官方文档。
注意有些云文档比如思维导图,PPT等是不支持用sdk导出的(2025-01-17)。
遍历代码部分示例:
func startRecursive(fileData *larkdrive.File, localPath string) error {
if *(fileData.Type) != "folder" {
err := AutoDetectFileTypeToDownload(fileData, localPath)
if err != nil {
fmt.Println("重试下载")
err := AutoDetectFileTypeToDownload(fileData, localPath)
if err != nil {
err := WriteLinesToFile("E:/batchdownloadcloud/"+user.UnableToDownloadTxtName, []string{localPath + "/" + *(fileData.Name)})
if err != nil {
fmt.Printf("写入文件异常")
return err
}
}
}
return nil
}
fls := GetFolderMetaList(*(fileData.Token))
if fls == nil {
return errors.New("无法获取文件夹清单")
}
var fileNames = []string{}
for _, data := range fls.Data.Files {
isR := false
if *(data.Type) != "folder" {
fmt.Println("检索: ", localPath+"/"+*(fileData.Name)+"/"+*(data.Name))
for _, v := range fileNames {
if v == *(data.Name) {
isR = true
fmt.Println("--------------重复--------------")
}
}
if !isR {
fileNames = append(fileNames, *(data.Name))
} else {
err := WriteLinesToFile("E:/batchdownloadcloud/repeat_name.txt", []string{localPath + "/" + *(fileData.Name) + "/" + *(data.Name)})
if err != nil {
fmt.Printf("写入文件异常")
return err
}
}
}
err := startRecursive(data, localPath+"/"+*(fileData.Name))
if err != nil {
return err
}
}
return nil
}
func AutoDetectFileTypeToDownload(fileData *larkdrive.File, localPath string) error {
fmt.Println("开始下载: ", localPath+"/"+*(fileData.Name))
err := CreateDirIfNotExist(localPath)
if err != nil {
return err
}
if *(fileData.Type) == "shortcut" {
fileData.Type = fileData.ShortcutInfo.TargetType
fileData.Token = fileData.ShortcutInfo.TargetToken
}
if *(fileData.Type) == "file" {
if IsExistFile(localPath + "/" + *(fileData.Name)) {
return nil
}
err := DownloadFileByFileToken(*(fileData.Token), localPath)
if err != nil {
return err
}
} else if *(fileData.Type) == "doc" || *(fileData.Type) == "sheet" || *(fileData.Type) == "bitable" || *(fileData.Type) == "docx" {
fe := *(fileData.Type)
if *(fileData.Type) == "sheet" || *(fileData.Type) == "bitable" {
fe = "xlsx"
} else if *(fileData.Type) == "docx" || *(fileData.Type) == "doc" {
fe = "docx"
}
if IsExistFile(localPath + "/" + *(fileData.Name) + "." + fe) {
return nil
}
err := StartDownloadCloudTask(fileData, localPath, fe)
if err != nil {
return err
}
} else {
err := WriteLinesToFile("E:/batchdownloadcloud/"+user.UnableToDownloadTxtName, []string{localPath + "/" + *(fileData.Name)})
if err != nil {
return err
}
}
fmt.Println("\033[32m下载成功\033[0m\n")
Count = Count + 1
fmt.Println("完成数: ", Count)
return nil
}
func StartDownloadCloudTask(fileData *larkdrive.File, localPath, fe string) error {
task := ExportCloudFile(fileData, fe)
timeout := time.After(time.Second * 60)
defer handlePanic(localPath + "/" + *(fileData.Name))
for {
select {
case <-timeout:
fmt.Println("任务超时,自动退出")
return fmt.Errorf("任务超时,自动退出")
default:
taskResult := GetTaskResult(*(task.Data.Ticket), *(fileData.Token))
if *(taskResult.Data.Result.JobStatus) == 0 {
// "E:/batchdownloadcloud/"
err := DownloadCloudFileByTaskFileToken(*(taskResult.Data.Result.FileToken), localPath, *(fileData.Name)+"."+fe)
if err != nil {
fmt.Println(err)
}
return nil
}
}
time.Sleep(time.Second * 1)
}
}
祝读者早日财富自由 😃