飞书 CLIdocs +media-download完全指南:下载文档素材与画板缩略图
【免费下载链接】cliThe official Lark/飞书 CLI tool, maintained by the larksuite team — built for humans and AI Agents. Covers core business domains including Messenger, Docs, Base, Sheets, Calendar, Mail, Tasks, Meetings, and more, with 200+ commands and 20+ AI Agent Skills.项目地址: https://gitcode.com/gh_mirrors/cli414/cli
导读:本文以 lark-cli 官方技能文档 lark-doc-media-download.md 为主体,系统讲解
docs +media-download命令的使用场景、参数语义、底层 API 调用链与扩展名自动补全机制。读完本文,你将掌握从飞书云文档中提取图片/文件素材 token 并安全下载到本地的完整流程,理解media与whiteboard两种资源类型的差异,并能独立排障权限(HTTP 403)与限流错误。
命令定位:它解决什么问题
docs +media-download是 lark-cli 中负责下载文档内嵌资源的命令,覆盖两类资源:
- 文档素材(media):文档中引用的图片或文件附件,通过
file_token定位; - 画板缩略图(whiteboard):白板(画板)页面的缩略图,通过
whiteboard_id定位。
它的核心特性是:当--output指定的路径不带扩展名时,命令会根据响应头的Content-Type(或Content-Disposition中的文件名)自动补全扩展名,避免手动猜测文件类型。
在源码中,该命令被注册为Service: "docs"、Command: "+media-download"、Risk: "read"(只读操作),定义于 shortcuts/doc/doc_media_download.go,并挂载在docs命令组下(见 shortcuts/doc/shortcuts.go)。
选择规则:download 与 preview 如何取舍
同一文档素材存在两个高度相关的命令,需要按用户意图区分:
| 场景 | 使用命令 |
|---|---|
| 用户明确说“下载素材” | docs +media-download |
| 用户只是想查看、预览图片或文件素材 | 优先使用docs +media-preview |
| 目标明确是画板 / whiteboard / 画板缩略图 | docs +media-download --type whiteboard(+media-preview不支持画板) |
从源码实现看,两个命令底层都调用了扩展名自动补全逻辑autoAppendDocMediaExtension(shortcuts/doc/doc_media_ext.go),但+media-preview仅支持file_token素材,不提供--type whiteboard分支;而+media-download的whiteboard分支会调用完全不同的 API(详见下文“底层调用链”)。
命令用法:三种典型场景
# 场景一:下载图片/文件素材(默认 type=media) lark-cli docs +media-download --token "Z1Fjxxxxxxxx" --output ./asset # 场景二:指定输出文件名(带扩展名则不会自动补全) lark-cli docs +media-download --token "Z1Fjxxxxxxxx" --output ./asset.png # 场景三:下载画板缩略图(whiteboard token) lark-cli docs +media-download --type whiteboard --token "wbcnxxxxxxxx" --output ./whiteboard关于“带扩展名不补全”的细节
源码 doc_media_download.go 展示了补全前的处理逻辑:
whiteboard类型有兜底扩展名fallbackExt = ".png"(画板缩略图必然是 PNG);media类型没有兜底,必须依赖响应头推断;- 若
--output以filepath.Ext判定为“已有显式扩展名”(且不是孤立的.),则跳过补全,直接按原路径保存; - 若路径以尾点
xxx.结尾,会先去掉尾点再补全(例如typed.→typed.csv),这一行为由测试 TestDocMediaDownloadAppendsExtensionForTrailingDotOutput 验证。
参数详解:必填项与可选语义
原文档的参数表如下:
| 参数 | 必填 | 说明 |
|---|---|---|
--token <token> | 是 | 资源 token:素材为file_token,画板为whiteboard_id |
--output <path> | 是 | 本地保存路径;不带扩展名会自动补全 |
--type <type> | 否 | media(默认)或whiteboard |
结合源码 doc_media_download.go 的 Shortcut 定义,还有两个原文档未列出的重要参数:
| 参数 | 必填 | 说明 |
|---|---|---|
--overwrite | 否(bool) | 是否覆盖已存在的输出文件。默认不覆盖:若目标文件已存在,命令返回FailedPrecondition错误并提示“use --overwrite to replace” |
--as | 否 | 身份选择:user或bot。该命令的AuthTypes声明为["user", "bot"],--as user代表用户本人下载,--as bot代表应用身份下载 |
参数校验与安全约束
执行时会先做两类校验(doc_media_download.go):
- token 校验:
validate.ResourceName(token, "--token")校验资源名合法性,失败返回InvalidArgument; - 输出路径安全校验:
runtime.ResolveSavePath(outputPath)只接受相对路径。这与共享规则一致——lark-shared 明确规定--file、--output等路径参数只接受 cwd 下的相对路径,传绝对路径会报unsafe file path(详见 skills/lark-shared/SKILL.md 安全规则第 5 条)。补全扩展名后的最终路径同样会再次校验。
下载完成后,命令以 JSON 形式输出结果(doc_media_download.go):
{ "saved_path": "./asset.png", "size_bytes": 20480, "content_type": "image/png" }底层调用链:两条 API 路径
--type决定最终请求的 OpenAPI 端点,这一逻辑在 doc_media_download.go 中明确:
--type media(默认):GET /open-apis/drive/v1/medias/{token}/download--type whiteboard:GET /open-apis/board/v1/whiteboards/{token}/download_as_image
两者的差异不仅在端点,还体现在权限预检上:
media模式在下载前会调用common.CheckDriveFileExportPermission(runtime, token)检查当前身份是否具备文档素材的导出权限(对应 dry-run 中的第一步请求GET /open-apis/drive/v1/permissions/{token}/members/auth?type=file&action=export);whiteboard模式跳过导出权限预检,直接请求画板下载端点。
这些差异可由 dry-run 输出与测试验证:
- TestDocMediaDownloadDryRunIncludesExportAuthBeforeDownload 断言
media模式 dry-run 包含 2 个 API:先导出权限检查、后媒体下载; - TestDocWhiteboardDownloadDryRunSkipsExportAuth 断言
whiteboard模式 dry-run 只有 1 个 API(/open-apis/board/v1/whiteboards/.../download_as_image)。
dry-run 预览能力来自 Shortcut 的DryRun字段(doc_media_download.go),配合 lark-shared 安全规则“目标命令支持--dry-run时,用--dry-run预览危险请求”,是调用前的推荐动作。
权限与 scope
该命令声明的权限 scope 为docs:document.media:download,并带有一个条件 scopeDrivePermissionMemberAuthScope(仅当需要导出权限检查时才会触发)。ConditionalScopes的存在意味着:对某些身份/资源,命令可能额外需要 Drive 成员权限,相关断言见 TestDocMediaDownloadDeclaresConditionalPermissionMemberAuthScope。
扩展名自动补全:完整优先级链
autoAppendDocMediaExtension(shortcuts/doc/doc_media_ext.go)按如下优先级推断扩展名:
- 显式扩展名优先:
--output已含非空扩展名(非.)时直接返回,不做任何推断; - Content-Type 映射:解析响应头
Content-Type,查表docMediaMimeToExt(doc_media_ext.go),覆盖常见类型,例如image/png → .png、image/jpeg → .jpg、application/pdf → .pdf、text/csv → .csv、video/mp4 → .mp4、application/vnd.openxmlformats-officedocument.wordprocessingml.document → .docx等; - Content-Disposition 文件名:若 Content-Type 未命中,则从
Content-Disposition: attachment; filename="..."中提取原文件名扩展名; - 兜底扩展名:仅
whiteboard类型使用.png兜底;media类型若无命中则保持原路径不变。
测试 TestDocMediaDownloadAppendsExtensionFromContentTypeMapping 与 TestDocMediaDownloadAppendsExtensionFromContentDispositionFilename 分别验证了第 2、3 条路径(后者模拟了Content-Type: application/octet-stream无法映射、但Content-Disposition带drive_registry_config_addition.csv文件名时补全为.csv的场景)。
token 从哪里来:配合lark-doc-fetch使用
素材 token 最常见的来源是lark-cli docs +fetch返回的文档内容。使用--doc-format xml(默认)时,内嵌资源会以 XML 标签形式出现(详见 lark-doc-fetch.md 中“处理文档内嵌资源”一节):
- 图片:
<img token="..." .../> - 文件:
<source token="..." name="..."/> - 画板:
<whiteboard token="..."/>
提取规则(与 fetch 文档一致):
- 标签内有
url属性时,仅当其为可信的公开 HTTPS URL(拒绝 userinfo、私有/回环/链路本地/组播/未指定地址 host)才可直接下载; - 无
url时提取token:预览用docs +media-preview,下载用docs +media-download; <whiteboard>一律提取 token 后走docs +media-download(fetch 文档明确指示画板不用 preview)。
由此形成完整工作流:
# 第一步:获取文档内容,提取 token lark-cli docs +fetch --doc "文档URL或token" --doc-format xml # 第二步:根据提取的 token 下载素材 lark-cli docs +media-download --token "Z1Fjxxxxxxxx" --output ./asset排障:两类典型错误
1. 权限错误:permission_denied与 HTTP 403
当导出权限预检失败时,命令返回permission_denied;当下载请求本身返回HTTP 403时,错误处理器withDocMediaDownloadRecoveryHint(shortcuts/doc/doc_errors.go)会在保留原始错误的同时,在hint中追加恢复指引:
Direct document media download returned HTTP 403. To preview the image or file content, try
lark-cli docs +media-preview --token <MEDIA_TOKEN> --output <path>.
也就是说:遇到 403 时按 hint 改用docs +media-preview预览内容(见 lark-doc-media-preview.md),这是文档推荐的降级路径。注意两点实现细节:
- 该 403 恢复提示仅对
media类型生效:whiteboard 走的是独立 API,withDocMediaDownloadRecoveryHint明确不把画板下载重定向到 media-preview; - 导出权限检查若返回的是权限类错误(如
LarkErrAppScopeNotEnabled、LarkErrTokenNoPermission、LarkErrUserScopeInsufficient),命令会打印 warning 后继续下载而不是直接中止(doc_media_download.go),相关行为由 TestDocMediaDownloadPermissionAuthScopeErrorsWarnAndContinue 验证;只有检查结果为“明确无权限”(allowed == false)时才在下载前中止。
2. 限流错误:停止立即重试,指数退避
限流判定涵盖三类信号(doc_errors.go):错误子类型SubtypeRateLimit、业务错误码99991400、HTTP 状态码429。命中后 hint 会追加:
Document media download was rate limited; stop immediate retries and retry later with exponential backoff.
即:停止立即重试,稍后按指数退避重试。相关测试包括 TestDocMediaDownloadHTTP429SuggestsBackoff、TestDocMediaDownloadExportAuthRateLimitPreservesAPIErrorAndSuggestsBackoff 等。
其他值得注意的行为
- 覆盖保护:默认若目标文件已存在则拒绝写入(
FailedPrecondition),需要显式加--overwrite。该行为由 TestDocMediaDownloadRejectsOverwriteWithoutFlag 验证,且覆盖检查发生在扩展名补全之后(对最终路径判断); - HTTP 错误先于落盘暴露:请求失败时不会创建半截文件,见 TestDocMediaDownloadRejectsHTTPErrorBeforeWrite;
- 导出被拒在下载前拦截:权限预检不通过时直接报错,避免无谓请求,见 TestDocMediaDownloadExportDeniedFailsBeforeDownload;
- 身份模型:命令支持
user/bot两种身份,--as的选择逻辑与身份权限差异(bot 查用户资源返回空成功而非报错)参见 skills/lark-shared/SKILL.md。
参考文档
- lark-doc-fetch — 获取文档内容,用于提取素材/画板 token
- lark-doc-media-preview — 预览素材(403 降级路径、与 download 的选择区分)
- lark-shared — 认证、
--as身份模型、相对路径安全规则与 JSON 输出契约
【免费下载链接】cliThe official Lark/飞书 CLI tool, maintained by the larksuite team — built for humans and AI Agents. Covers core business domains including Messenger, Docs, Base, Sheets, Calendar, Mail, Tasks, Meetings, and more, with 200+ commands and 20+ AI Agent Skills.项目地址: https://gitcode.com/gh_mirrors/cli414/cli
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考