UniDoc说明中心下载 .udoc
UNIDOC / KNOWLEDGE IN MOTION

公共接口与 UDOC 生成指南

从理解,到动手实现。步骤、协议与可复制的示例,都在这份文档中。

版本:2026-09-12

规范入口:https://app.unidoc.top/

本文只描述当前实现中已经存在并可验证的接口。内部 AI 编排协议、编辑器 DOM、
encodeUdocPackage() 等未导出的函数不属于公共兼容性承诺。

1. 能力总览

能力当前状态推荐用途
原始字节格式转换 HTTP API已提供Markdown、DOCX、UDOC 相互转换
iframe postMessage已提供宿主页面整档装载、读取 HTML/JSON
window.UniDocConverters已提供在 UniDoc 页面内调用格式转换
UDOC JSON Authoring v1已提供AI/人工生成可审阅的非压缩交换稿
Python tools/udoc_json.py已提供校验、打包和解包 .udoc.json
公网 UDOC JSON <-> UDOC HTTP API已提供受认证、配额和请求尺寸限制的转换
细粒度文档 AI MCP(OAuth + JSON-RPC)已提供按文稿、段落和原生批注进行受保护的 AI 读写

这里的“公共”表示接口不依赖 UniDoc 内部模块变量,并不代表无限额、无认证或永久
兼容。生产部署可按账号、来源、配额和风控策略收紧访问。

2. 公共格式转换 HTTP API

所有接口都使用原始请求体,不使用 multipart/form-data。Markdown 必须是 UTF-8;
二进制接口直接传文件字节。单次转换的当前上限为 200 MiB。

方法与路径请求体成功响应
POST /convert/md2unidocUTF-8 MarkdownUDOC,application/vnd.unidoc
POST /convert/md2unidoUTF-8 Markdown同上,兼容别名
POST /convert/md2docxUTF-8 MarkdownDOCX
POST /convert/md2htmlUTF-8 Markdown独立 HTML
POST /convert/unidoc2mdUDOC 字节(纯文字或已托管静态图)UTF-8 Markdown;待截图或待托管媒体返回 422
POST /convert/unidoc2wordUDOC 字节DOCX
POST /convert/udoc2docxUDOC 字节DOCX,兼容路径
POST /convert/docx2udocDOCX 字节UDOC

示例:

curl --fail-with-body \
  -H "Content-Type: text/markdown; charset=utf-8" \
  --data-binary @README.md \
  https://app.unidoc.top/convert/md2unidoc \
  --output README.udoc

curl --fail-with-body \
  -H "Content-Type: application/octet-stream" \
  --data-binary @README.udoc \
  https://app.unidoc.top/convert/unidoc2word \
  --output README.docx

失败响应通常是 JSON:

{ "error": "错误说明" }

内部 Brotli / ZIP 部件接口只负责单个部件的压缩或解压,不能把一份
解包 JSON 自动组装成完整 UDOC,因此不应当被当作文档生成 API。

3. 页面内转换 API

在 UniDoc 页面同一个 JavaScript 上下文中,可以使用:

const html = await window.UniDocConverters.md2html('# 标题');
const udocBlob = await window.UniDocConverters.md2unidoc('# 标题');
const markdown = await window.UniDocConverters.unidoc2md(await file.arrayBuffer());
const wordBlob = await window.UniDocConverters.unidoc2word(await file.arrayBuffer());

同时存在等名快捷函数:md2docxmd2htmlmd2unidocmd2unido
unidoc2mdunidoc2word。文本结果是 Promise<string>,文档结果是
Promise<Blob>

浏览器 unidoc2md 统一走静态文章流程:动态 HTML、Canvas 和公式转成 PNG,图片发布到当前用户的 OmniDoc/R2,再写入可撤销的公开地址。图片发布需要登录及云文件写入、读取、分享权限。纯 HTTP POST /convert/unidoc2md 没有浏览器画面,待截图或待托管的输入返回 422 portable_media_requires_browser。操作和限制见 公众号排版与通用 Markdown

这些函数属于 UniDoc 页面,不会自动注入任意第三方网页。跨站宿主应使用下一节的
iframe 协议或直接调用 HTTP API。

4. iframe 文档桥

完整的 URL 参数、实时缩放、全屏回退和可运行示例见 内嵌接入文档

嵌入入口:

<iframe
  id="unidoc"
  src="https://app.unidoc.top/?embed=1&embedView=flow"
  title="UniDoc 编辑器">
</iframe>

宿主向 iframe 发送消息:

const frame = document.getElementById('unidoc');
const origin = 'https://app.unidoc.top';

frame.contentWindow.postMessage({
  type: 'unidoc:set',
  id: 'load-1',
  doc: {
    format: 'udoc',
    version: 3,
    unidoc_type: 'docs',
    title: 'AI 生成文档',
    blocks: [
      { para_id: 1, html: '<h1>标题</h1>' },
      { para_id: 2, html: '<p>正文</p>' }
    ]
  }
}, origin);

支持的消息:

方向type字段与语义
宿主 → iframeunidoc:setdoc 为解包 JSON、html 为待导入 HTML,或 udoc 为原生 udoc3:BASE64 / 交换 JSON 字符串
宿主 → iframeunidoc:get请求流式、不可编辑 HTML;可带 id
宿主 → iframeunidoc:get-udoc推荐传 format:'udoc3' 取原生包;不传则取交换 JSON 字符串;可带 id
iframe → 宿主unidoc:ready正文桥可用,URL 模板处理已结束;不代表最终视觉排版完成
iframe → 宿主unidoc:content-setunidoc:set 已应用
iframe → 宿主unidoc:contenthtml 返回值
iframe → 宿主unidoc:udoc原生路径返回 format:'udoc3'udoc:'udoc3:BASE64';否则 udoc 为交换 JSON 字符串
iframe → 宿主unidoc:change文档发生变化,约 400 ms 防抖
iframe → 宿主unidoc:errorerror 为错误说明

宿主必须校验消息来源:

window.addEventListener('message', event => {
  if (event.origin !== 'https://app.unidoc.top') return;
  if (event.source !== document.getElementById('unidoc').contentWindow) return;
  console.log(event.data);
});

匿名协作分享链接是服务端强制只读的。此时键盘、粘贴、拖放、编辑工具和
unidoc:set 都不能修改文档;WebSocket 票据同样拒绝任何更新。读取、复制、缩放、
放映和导出仍可使用。

5. UDOC JSON Authoring v1:AI 可写交换格式

.udoc.json 是面向 AI、脚本和代码审阅的非压缩交换格式,MIME 为
application/vnd.unidoc.udoc+json。它不是 UniDoc 的本地保存格式:用户执行保存、
另存为、Vault 保存或 OmniDoc 云保存时,系统必须重新验证并编码为带有正式容器签名的
UDOC 混合压缩包。把普通 JSON 改名为 .udoc 必须拒绝,不能降级读取。

正式 JSON Schema:
schemas/udoc-json/v1.json,公网 $id
https://app.unidoc.top/schemas/udoc-json/v1.json。最小可用文档:

{
  "$schema": "https://app.unidoc.top/schemas/udoc-json/v1.json",
  "format": "udoc",
  "version": 3,
  "unidoc_type": "docs",
  "basename": "AI生成报告",
  "page": {
    "size": "A4",
    "orientation": "portrait",
    "marginPreset": "normal",
    "margins": { "top": 76, "right": 76, "bottom": 76, "left": 76 }
  },
  "blocks": [
    { "para_id": 1, "html": "<h1>标题</h1>" },
    { "para_id": 2, "html": "<p>正文</p>" }
  ]
}

作者必须遵守这些可执行约束:

  • formatversionunidoc_type 必须分别严格为 udoc、整数 3docs
  • para_id 是正整数,在整份文档中唯一且长期稳定;不要使用 "b12" 或数字字符串;
  • 每个 blocks[].html 必须只有一个顶层元素,例如一个 <p><h1><table>

<div>;完整网页不能伪装成普通段落块;

  • 普通块禁止 <script>、iframe/插件标签、on* 事件属性、javascript: URL 和 CSS

活动表达式;完整可执行网页应放在隔离沙箱的 textboxes[kind=htmledit].config.html

  • 内联图片、音视频和附件使用 data: URL。打包器把它们改写为

media/<sha256>.<ext>attachments/<sha256>.<ext>,去重并生成关系;

  • 解包时也可使用顶层 resources 对象:键是内容寻址包内路径,值是自包含 data URL。

路径哈希必须与解码后的字节完全一致;

  • wordCachewordSourcePartswordSettings 是 Word 导入产生的派生缓存,AI 稿不得

写入。转换器会从 UDOC 导出交换稿时剔除它们;

  • 当前实现限制为 100,000 块、单块 2 MiB、HTML 合计 64 MiB、单资源 32 MiB、

资源合计 64 MiB、JSON 嵌套 48 层。服务端还可按账号设置更低配额。

自由 HTML 应作为独立网页源码,而不是拆成伪段落:

{
  "id": "site-home",
  "kind": "htmledit",
  "config": {
    "webPage": true,
    "freeHtmlSource": true,
    "html": "<!doctype html><html><head><title>My App</title></head><body><h1>Hello</h1></body></html>"
  }
}

打包后 config.html 会变为内容寻址的 embeds/<sha256>.html,原始网页字节进入
UDOC。编辑预览必须继续使用隔离源沙箱;不得把 allow-scripts
allow-same-origin 同时授予同一个不可信 iframe。

5.1 本地 CLI 和 Python API

依赖:python -m pip install brotli。CLI:

python tools/udoc_json.py validate report.udoc.json
python tools/udoc_json.py pack report.udoc.json report.udoc
python tools/udoc_json.py unpack report.udoc report.udoc.json --pretty
python tools/udoc_json.py unpack report.udoc report-inline.udoc.json --inline-media --pretty

unpack 默认把包内二进制写成顶层 resources 的 data URL,仍是一份自包含 JSON;
--inline-media 会进一步把图片、视频、音频和附件引用放回对应 HTML/对象字段。
HTML embeds 与其他源码资源仍保留在 resources,避免把整页源码塞进 HTML 属性。

模块 API:

from tools.udoc_json import (
    load_authoring_json,
    validate_authoring_document,
    authoring_to_package,
    package_to_authoring,
)

draft = load_authoring_json("report.udoc.json")
validated_copy = validate_authoring_document(draft)
authoring_to_package(validated_copy, "report.udoc")
roundtrip = package_to_authoring("report.udoc", inline_media=True)

第三方生成文档应优先使用上述版本化 Authoring API;
它生成相同的严格 UDOC 容器。不要自行拼接 Brotli/ZIP 字节,也不要调用页面内部
encodeUdocPackage(),后者不是版本化公共 API。

5.2 受认证的公网转换 API

三个端点当前只接受 app.unidoc.top 的已登录浏览器会话,并强制执行精确同源/CSRF
校验;服务端尚未把 OAuth Bearer Token 暴露为这一组接口的公共认证方式,也不提供
匿名跨域转换代理。请求不得引用远程媒体 URL,调用方应先将资源转为 data URL,避免
服务器替调用方任意抓取公网或内网地址。

方法与路径请求体成功响应
POST /validate/udoc-jsonUTF-8 .udoc.jsonJSON 校验结果与块数
POST /convert/udoc-json2udocUTF-8 .udoc.json严格 UDOC 二进制容器
POST /convert/udoc2udoc-jsonUDOC 字节.udoc.jsonapplication/vnd.unidoc.udoc+json

POST /convert/udoc2udoc-json?inline_media=1 请求内联图片、音视频和附件。转换失败返回
非 2xx JSON,例如 { "error": "$.blocks[2].para_id: 重复的 para_id:2" }

// 在已登录的 https://app.unidoc.top 页面中调用;跨域页面会被拒绝。
const response = await fetch('/convert/udoc-json2udoc', {
  method: 'POST',
  credentials: 'same-origin',
  headers: { 'Content-Type': 'application/vnd.unidoc.udoc+json' },
  body: JSON.stringify(draft),
});
if (!response.ok) throw new Error((await response.json()).error);
const udoc = await response.blob();

服务端收到 JSON 后必须复用同一套校验器,并验证生成文件的容器签名、版本和完整性;不能
信任文件扩展名,也不能把 JSON 原样写入 Vault/OmniDoc 存储。

5.3 保存原生 HTML 与发布静态网站

UniDoc 保存或导出的原生 HTML 中,“继续编辑”入口固定为
https://app.unidoc.top/。当前页面域名、旧部署域名、文档内 <meta> 或 HTML 内容都
不能覆盖该地址,因而离线打开导出文件时也会回到规范生产入口。

自由 HTML 可以从编辑器发布为个人静态网站。生产发布按以下顺序执行:

  1. 将当前文档编码为严格 UDOC 容器,保存到当前用户的 OmniDoc 专属空间;
  2. 取得该对象的 sourceObjectId
  3. 以同源登录会话调用 POST /sites/publish,提交自包含 HTML、名称和

sourceObjectId

  1. 服务端向 OmniDoc 精确验证当前用户对该对象的读取权限,验证通过后才生成

https://sites.unidoc.top/p/<id>/ 快照。

v1 只发布一份自包含 index.html;样式、脚本和媒体应内联,不支持上传可枚举目录或
任意服务端路径。sites.unidoc.top 是能力隔离的只读来源:它只响应已发布页面,不
暴露编辑器、登录、云文件或转换接口。

公开快照允许脚本、表单和下载,但以不透明来源沙箱运行,不能把
sites.unidoc.top 的 Cookie 或 localStorage 当作页面自己的持久化存储。
“继续编辑”打开的 app.unidoc.top 新窗口不继承该沙箱;HTML 通过一次性的
窗口身份与随机令牌握手传入,不会把公开页面变成匿名服务端抓取代理。
源对象校验表示“当前用户可读取这个对象”,不等于服务端已经验证对象所有权、
UDOC 内容或它与 HTML 快照的语义对应关系;严格 UDOC 源保存由官方编辑器流程保证。

6. 对 AI/MCP 的适配评价

当前设计的优点是:文档模型是结构化块而不是画布像素,para_id 稳定,整档导入/导出
和二进制转换都有确定边界,因此浏览器 AI 比操作传统富文本 DOM 更容易获得可复现结果。

细粒度 MCP 已作为可选能力层提供。它不参与编辑器启动,也不替换 iframe 桥;客户端只有
在需要 AI 操作时才连接 /mcp。MCP 使用已验证的 UniDoc 会话或 OAuth Bearer grant,匿名
只读分享不能获得工具调用权限。写操作都携带 expectedRevision + requestId,因此并发修改
会返回可处理的冲突,重复请求会得到同一结果。

MCP 的工具、OAuth 发现地址、原生批注锚定和客户端示例见
docs/MCP_PROTOCOL.mddocs/AI_MCP_INTEGRATION.md
iframe 桥仍适合宿主页面整档装载;MCP 适合 AI 按 para_id、批注 ID 和版本进行局部操作。

7. 相关规范

  • UDOC 容器规范:容器布局、分片、压缩和完整性规则。
  • UniDoc MCP 协议:OAuth、JSON-RPC、细粒度工具和原生批注锚定。
  • AI MCP 接入示例:客户端配置、调用顺序和 WASM/iframe 边界。
  • EMBED_MODE.md:嵌入模式、URL 模板和视图行为。
  • COLLABORATION_PROTOCOL.md:协作房间、票据、只读分享和部署边界。