前言
在飞书文档开发中,我们通常会面临两种选择:
- 使用官方 OpenAPI - 直接调用飞书开放平台接口
- 使用封装好的 Skills - 如 feishu-create-doc、feishu-update-doc 等
本文将深入对比这两种方式在图片插入和 Markdown 转表格场景下的差异,帮助开发者做出正确选择。
一、图片插入对比
1.1 官方API方式(三步法)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| import requests
url = f"https://open.feishu.cn/open-apis/docx/v1/documents/{doc_id}/blocks/{parent_id}/children" data = { "index": -1, "children": [{ "block_type": 27, "image": {"height": 100, "width": 100} }] } resp = requests.post(url, headers=headers, json=data) image_block_id = resp.json()["data"]["children"][0]["block_id"]
url = "https://open.feishu.cn/open-apis/drive/v1/medias/upload_all" files = {"file": ("image.png", open("image.png", "rb"), "image/png")} data = { "file_name": "image.png", "parent_type": "docx_image", "parent_node": image_block_id, "size": str(file_size) } resp = requests.post(url, headers=headers, files=files, data=data) file_token = resp.json()["data"]["file_token"]
url = f"https://open.feishu.cn/open-apis/docx/v1/documents/{doc_id}/blocks/{image_block_id}" data = {"replace_image": {"token": file_token}} resp = requests.patch(url, headers=headers, json=data)
|
特点:
- ✅ 完全控制流程
- ✅ 可以处理大图片
- ❌ 代码复杂,需要处理多个API
- ❌ 需要管理多个权限
1.2 Skills方式
1 2 3 4 5 6
| feishu_update_doc( doc_id="xxx", mode="append", markdown="" )
|
实际执行过程:
- Skill 内部调用官方API创建图片块
- 尝试上传图片(可能失败)
- 返回结果
特点:
- ✅ 代码简洁,一行搞定
- ✅ 自动处理 Markdown 语法
- ❌ 图片插入经常失败(见下文分析)
- ❌ 错误信息不明确
1.3 关键差异对比
| 维度 |
官方API |
Skills |
| 代码复杂度 |
高(30+行) |
低(1行) |
| 成功率 |
高(权限正确时100%) |
低(经常失败) |
| 错误可控性 |
高(每步可监控) |
低(黑盒) |
| 灵活性 |
高(可自定义) |
低(固定流程) |
| 权限要求 |
明确(需开通多个) |
模糊 |
1.4 Skills图片插入失败原因分析
根本原因:Skills 通常使用 im/v1/images API 上传图片,然后尝试嵌入文档。
1 2 3
| url = "https://open.feishu.cn/open-apis/im/v1/images"
|
正确做法:必须使用 drive/v1/medias/upload_all API,并指定 parent_type="docx_image" 和 parent_node={图片块ID}。
结论:图片插入场景,推荐使用官方API三步法。Skills 适合纯文本内容,不适合图片。
二、Markdown转表格对比
2.1 官方API方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| url = f"https://open.feishu.cn/open-apis/docx/v1/documents/{doc_id}/blocks/{parent_id}/children" data = { "children": [{ "block_type": 31, "table": { "property": { "row_size": 3, "column_size": 2 } } }] }
|
特点:
- ✅ 完全控制表格样式
- ❌ 代码极其复杂
- ❌ 需要处理单元格合并等细节
2.2 Skills方式
1 2 3 4 5 6 7 8 9 10 11 12 13
| markdown = """ | 日期 | 汇率 | 变化 | |------|------|------| | 2025-12-01 | 0.0450 | - | | 2026-01-27 | 0.0457 | ↑ | | 2026-03-11 | 0.0432 | ↓ | """
feishu_create_doc( title="汇率数据", markdown=markdown )
|
实际执行过程:
- Skill 解析 Markdown 表格
- 自动转换为飞书文档表格格式
- 调用API创建表格块和单元格
特点:
- ✅ 一行代码搞定
- ✅ 自动处理格式转换
- ✅ 支持复杂表格
- ❌ 样式定制受限
2.3 关键差异对比
| 维度 |
官方API |
Skills |
| 代码量 |
50+行 |
1行 |
| 开发效率 |
低 |
高 |
| 样式定制 |
完全可控 |
有限 |
| 复杂表格支持 |
需手动处理 |
自动处理 |
| 维护成本 |
高 |
低 |
2.4 Markdown转表格的注意事项
表格语法兼容性
- 标准Markdown表格(支持)
- 对齐语法(部分支持)
复杂内容限制
- 单元格内换行(可能不支持)
- 单元格内列表(可能不支持)
性能考虑
- 大表格(>100行)建议使用分批插入
- Skills 可能会超时
结论:Markdown转表格场景,强烈推荐使用 Skills。官方API太繁琐,除非需要特殊样式。
三、选择决策树
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| 开始 │ ├─ 需要插入图片? │ ├─ 是 → 使用官方API三步法 │ └─ 否 → 继续 │ ├─ 需要Markdown转表格? │ ├─ 是 → 使用 Skills │ └─ 否 → 继续 │ ├─ 需要精细控制样式? │ ├─ 是 → 使用官方API │ └─ 否 → 使用 Skills │ └─ 纯文本内容? └─ 使用 Skills(最简单)
|
四、最佳实践建议
4.1 混合使用策略
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
def create_doc_with_content(title, markdown_content, image_paths=None): """创建包含文字和图片的文档""" doc = feishu_create_doc( title=title, markdown=markdown_content ) if image_paths: for img_path in image_paths: insert_image_via_api(doc["doc_id"], img_path) return doc
|
4.2 权限管理清单
| 功能 |
所需权限 |
备注 |
| 创建文档 |
docx:document |
基础权限 |
| 插入文字 |
docx:document |
同上 |
| 插入图片 |
docx:document + drive:drive + docs:document.media:upload |
必须三个都有 |
| 读取文档 |
docx:document |
只读 |
| 协作者管理 |
docs:permission.member |
可选 |
4.3 错误处理建议
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| def safe_insert_image(doc_id, image_path): """安全的图片插入,带重试机制""" max_retries = 3 for i in range(max_retries): try: if not check_permission(doc_id): raise PermissionError("无编辑权限") block_id = create_image_block(doc_id) file_token = upload_image(block_id, image_path) update_image_block(doc_id, block_id, file_token) return True except PermissionError as e: print(f"权限错误: {e}") break except Exception as e: print(f"第{i+1}次尝试失败: {e}") if i == max_retries - 1: raise return False
|
五、总结
| 场景 |
推荐方案 |
理由 |
| 图片插入 |
官方API |
Skills 成功率低,官方API三步法可靠 |
| Markdown转表格 |
Skills |
开发效率高,代码简洁 |
| 纯文本内容 |
Skills |
最简单,一行搞定 |
| 复杂样式定制 |
官方API |
完全可控 |
| 批量操作 |
混合方案 |
Skills处理文本,API处理图片 |
核心原则
- 图片用API,文本用Skills
- 权限提前开通,避免运行时错误
- 做好错误处理和重试机制
参考链接
本文基于实际项目经验编写,如有更新请以飞书官方文档为准。