Install
openclaw skills install feishu-cloud-drive基于飞书官方 API 的云盘管理技能,支持文件列表查询、上传、下载、文件夹创建、权限管理、文件搜索、统计信息、快捷方式、复制移动等完整功能。参考 feishu-drive 技能开发,修复了原技能中的 API 调用错误,并新增了自动化权限管理功能。
openclaw skills install feishu-cloud-drive本技能参考了社区中的 feishu-drive 技能,但在实现过程中发现原技能存在以下问题:
GET /open-apis/drive/v1/files/:token/children 应改为 drive/explorer/v2 版本)因此基于飞书官方文档重新开发了此技能,修复了所有已知问题,并新增了权限管理功能。
你是飞书云盘管理专家,负责通过飞书官方 API 实现文件列表查询、上传、下载和文件夹管理。
设置环境变量:
export FEISHU_APP_ID="your_app_id"
export FEISHU_APP_SECRET="your_app_secret"
export FEISHU_ROOT_FOLDER_TOKEN="your_folder_token" # 可选:指定默认根目录
在使用技能之前,先指定一个根目录,后续所有操作都以此目录为基准:
from feishu_drive_client import create_client, FeishuDriveClient
import os
# 方式1:使用便捷函数(推荐)
# 自动从环境变量 FEISHU_APP_ID, FEISHU_APP_SECRET, FEISHU_ROOT_FOLDER_TOKEN 读取
client = create_client()
# 方式2:手动创建客户端,从环境变量读取
client = FeishuDriveClient(
app_id=os.getenv("FEISHU_APP_ID"),
app_secret=os.getenv("FEISHU_APP_SECRET"),
root_folder_token=os.getenv("FEISHU_ROOT_FOLDER_TOKEN") # 可选
)
# 方式3:手动指定所有参数
client = FeishuDriveClient(
app_id="your_app_id",
app_secret="your_app_secret",
root_folder_token="your_folder_token" # 可选
)
# 方式4:后续动态设置根目录
client = FeishuDriveClient(app_id, app_secret)
client.set_root_folder("your_folder_token")
设置根目录后,所有操作默认在根目录下进行:
# 在根目录下创建子文件夹(无需传入 folder_token)
result = client.create_folder("新文件夹")
# 在根目录下上传文件
result = client.upload_file("/path/to/file.jpg")
# 列出根目录内容
result = client.list_folder()
# 如需操作其他目录,可临时指定 folder_token
result = client.create_folder("其他位置的文件夹", parent_folder_token="其他token")
优先级规则:传入的 folder_token > 设置的 root_folder_token > 空字符串(根目录)
| 项目 | 值 |
|---|---|
| Base URL | https://open.feishu.cn/open-apis/drive/v1 |
| 认证方式 | Authorization: Bearer {tenant_access_token} |
| Content-Type | application/json (文件上传用 multipart/form-data) |
接口说明: 获取用户"我的空间"根文件夹的元数据
GET /open-apis/drive/explorer/v2/root_folder/meta
响应示例:
{
"code": 0,
"data": {
"token": "nodcnXXXXXX",
"id": "7110173013420512356",
"user_id": "7103496998321312356"
},
"msg": "success"
}
注意事项:
drive:drive 或 drive:drive.metadata:readonly接口说明: 在指定父文件夹下创建新文件夹
POST /open-apis/drive/v1/files/create_folder
请求参数:
{
"name": "文件夹名称",
"folder_token": "父文件夹token"
}
响应示例:
{
"code": 0,
"data": {
"token": "fldcnXXXXXX",
"url": "https://xxx.feishu.cn/drive/folder/fldcnXXXXXX",
"name": "文件夹名称"
}
}
注意事项:
folder_token 为父文件夹 token,可以通过 URL 获取或通过 API 查询接口说明: 获取指定文件夹下的文件和子文件夹列表
推荐方法 (Explorer v2):
GET /open-apis/drive/explorer/v2/folder/:folder_token/children
响应示例:
{
"code": 0,
"data": {
"children": {
"file_token1": {
"token": "file_token1",
"name": "文件名",
"type": "file"
},
"folder_token1": {
"token": "folder_token1",
"name": "文件夹名",
"type": "folder"
}
},
"parentToken": "folder_token"
},
"msg": "success"
}
注意事项:
type 为 folder 表示文件夹,为 file 表示文件children 是字典格式,不是数组接口说明: 上传文件到指定文件夹
POST /open-apis/drive/v1/files/upload_all
Content-Type: multipart/form-data
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file | binary | 是 | 文件二进制内容 |
| file_name | string | 是 | 文件名 |
| parent_type | string | 是 | 父节点类型,explorer = 云空间 |
| parent_node | string | 是 | 父节点 token(文件夹 token) |
| size | number | 是 | 文件大小(字节) |
响应示例:
{
"code": 0,
"data": {
"file_token": "file_token"
}
}
注意事项:
parent_type 使用 explorer 表示云空间文件夹size 参数必填,建议在上传前计算文件大小接口说明: 根据文件 token 下载文件
GET /open-apis/drive/v1/files/:file_token/download
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file_token | string | 是 | 文件 token |
响应:
注意事项:
Authorization: Bearer {tenant_access_token}接口说明: 通过邮箱或手机号获取用户的 open_id
POST /open-apis/contact/v3/users/batch_get_id
请求参数:
{
"emails": ["user@example.com"],
"mobiles": ["13800138000"],
"include_resigned": false
}
响应示例:
{
"code": 0,
"data": {
"user_list": [
{
"user_id": "ou_xxxxxxxxx",
"email": "user@example.com",
"status": {
"is_activated": true,
"is_resigned": false
}
}
]
}
}
注意事项:
contact:user.id:readonly 权限user_id 就是 open_id接口说明: 为文件夹添加用户权限
POST /open-apis/drive/v1/permissions/:token/members?type=folder
请求参数:
{
"member_type": "openid",
"member_id": "ou_xxxxxxxxx",
"perm": "full_access",
"perm_type": "container",
"type": "user"
}
权限类型:
| perm | 说明 |
|---|---|
view | 可阅读 |
edit | 可编辑 |
full_access | 可管理(完全访问) |
注意事项:
member_id 必须是 open_id,不能是 user_id接口说明: 获取文件的详细信息
GET /open-apis/drive/v1/files/:file_token
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file_token | string | 是 | 文件或文件夹 token |
响应示例:
{
"code": 0,
"data": {
"token": "token",
"name": "名称",
"type": "file/folder",
"size": 1024,
"created_time": 1234567890,
"owner": {
"open_id": "xxx"
}
}
}
接口说明: 删除指定文件夹(删除后进入回收站)
DELETE /open-apis/drive/v1/files/:folder_token?type=folder
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| folder_token | string | 是 | 文件夹 token |
| type | string | 是 | 固定值 folder |
响应示例:
{
"code": 0,
"msg": "success"
}
注意事项:
Python 示例:
# 删除文件夹(带确认提示)
def delete_folder_with_confirm(client, folder_token, folder_name, confirmed=False):
# 1. 显示文件夹信息
print(f"即将删除文件夹: {folder_name}")
print(f"Token: {folder_token}")
# 2. 请求用户确认(在自主运行环境中,通过参数传递确认状态)
if not confirmed:
print("请设置 confirmed=True 确认删除")
return
# 3. 执行删除
result = client.delete_folder(folder_token)
if result.get("code") == 0:
print("删除成功!文件夹已进入回收站")
else:
print(f"删除失败: {result.get('msg')}")
# 使用示例 - 必须显式确认
delete_folder_with_confirm(client, "folder_token", "测试文件夹", confirmed=True)
接口说明: 删除指定文件(删除后进入回收站)
DELETE /open-apis/drive/v1/files/:file_token?type=file
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| file_token | string | 是 | 文件 token |
| type | string | 是 | 固定值 file |
注意事项:
接口说明: 将文件或文件夹移动到指定文件夹
POST /open-apis/drive/v1/files/:file_token/move
请求参数:
{
"type": "file",
"destination_folder_token": "目标文件夹token"
}
类型说明:
| type 值 | 说明 |
|---|---|
file | 普通文件 |
folder | 文件夹 |
doc | 文档 |
sheet | 电子表格 |
bitable | 多维表格 |
docx | 新版文档 |
接口说明: 复制文件到指定文件夹(异步操作)
POST /open-apis/drive/v1/files/:file_token/copy
请求参数:
{
"type": "file",
"destination_folder_token": "目标文件夹token",
"name": "复制后的新名称(可选)"
}
响应示例:
{
"code": 0,
"data": {
"ticket": "task_ticket_xxx"
},
"msg": "success"
}
注意事项:
check_task_status(ticket) 查询复制进度接口说明: 批量获取多个文件的元数据信息
POST /open-apis/drive/v1/metas/batch_query
请求参数:
{
"file_tokens": ["token1", "token2", "token3"]
}
限制: 最多 100 个文件 token
接口说明: 获取文件的阅读、点赞、评论等统计信息
POST /open-apis/drive/v1/files/:file_token/statistics
响应示例:
{
"code": 0,
"data": {
"uv": 100,
"pv": 200,
"like_count": 10,
"comment_count": 5
}
}
接口说明: 获取谁访问了该文件的记录
POST /open-apis/drive/v1/files/:file_token/view_records
请求参数:
{
"page_size": 50,
"page_token": "可选的分页token"
}
接口说明: 在指定文件夹创建文件的快捷方式
POST /open-apis/drive/v1/files/create_shortcut
请求参数:
{
"file_token": "源文件token",
"folder_token": "目标文件夹token",
"type": "file"
}
接口说明: 在云空间中搜索文件
POST /open-apis/suite/docs-api/search/object
请求参数:
{
"search_key": "title",
"search_value": "搜索关键词",
"page_size": 50
}
search_key 说明:
| 值 | 说明 |
|---|---|
title | 按标题搜索 |
content | 按内容搜索 |
注意事项:
user_access_token,不支持 tenant_access_token接口说明: 查询复制、移动等异步操作的任务状态
GET /open-apis/drive/v1/files/task_check?ticket=xxx
请求参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| ticket | string | 是 | 异步任务 ticket |
响应示例:
{
"code": 0,
"data": {
"status": "success",
"file_token": "新文件token"
}
}
status 说明:
| 值 | 说明 |
|---|---|
pending | 等待中 |
processing | 处理中 |
success | 成功 |
failed | 失败 |
| 错误码 | 含义 | 解决方案 |
|---|---|---|
| 0 | 成功 | — |
| 99991663 | token 过期 | 重新获取 tenant_access_token |
| 1061002 | 无权限 | 检查应用权限和文件夹授权 |
| 1061001 | 文件/文件夹不存在 | 检查 token 是否正确 |
| 1061045 | 文件大小超限 | 使用分片上传或减小文件大小 |
| 404 | 文件不存在 | 常见原因:使用了字典 key 而非 item['token'],参见下方常见错误 |
在获取文件夹内容后尝试下载文件时,出现 404 错误。
混淆了飞书 API 返回数据结构中的两种"token":
{
"children": {
"nodcn5OkQG6q8Y4SuSncukJmCtc": { // ← 错误:这是字典的 key,不是下载用 token
"token": "KdLTbke3BoN85RxQg5qcswFYnah", // ← 正确:这才是下载用的 token
"name": "Screenshot_20260323-115306.png",
"type": "file"
}
}
}
result = client.list_folder(folder_token)
children = result["data"]["children"]
for token, item in children.items(): # ← token 实际是 dict_key (nodcn...)
file_token = token # ← 错误!这会导致 404
client.download_file(file_token, "/path/to/file")
result = client.list_folder(folder_token)
children = result["data"]["children"]
for dict_key, item in children.items(): # dict_key 是 nodcn...
file_token = item["token"] # ← 正确!从 item 中获取 token
client.download_file(file_token, "/path/to/file")
# 或者直接使用 list_all() 方法,它已经处理了这个问题
files = client.list_all(folder_token)
for file in files:
client.download_file(file["token"], f"/path/to/{file['name']}")
| 位置 | 值 | 用途 |
|---|---|---|
| 字典 key | nodcn5OkQG6q8Y4SuSncukJmCtc | 仅用于迭代,通常不使用 |
item['token'] | KdLTbke3BoN85RxQg5qcswFYnah | 下载、删除、复制、移动等所有操作 |
确保应用已获取以下权限:
drive:file:read - 读取文件drive:file:write - 写入文件drive:folder:read - 读取文件夹drive:folder:write - 创建文件夹contact:user.id:readonly - 获取用户 ID(用于权限管理)get_file_info 测试 token 是否可用