外观
开始:最小集成
本页给出一个最小可跑的「打开一份文档 → 编辑 → 保存回自有存储」闭环。跑通它,即建立了接入 ATKONOFFICE 的完整心智。更细的每个功能写法见 通用控制。
接入分布在两侧
| SDK | 坐标 / 包名 | 运行位置 | 职责 |
|---|---|---|---|
| 服务端 SDK | cn.atkon:atkonoffice-sdk(Maven Central) | 集成方 Spring Boot 后端 | 生成宿主页、签发拉起链、接收保存、对接存储、产品授权 |
| 浏览器端 SDK | @atkonoffice/sdk(npm) | 集成方业务网页 | 拉起编辑窗口、透传业务参数、订阅生命周期事件 |
关键原则贯穿始终:
- 签名密钥永不下发浏览器——拉起链在业务后端签发,浏览器只发起
open(); - 文档字节只走企业内部链路——从哪取由集成方实现的解析器决定,往哪存由 save 端点决定;
- 打开模式由服务端裁决——浏览器不传打开模式,由后端在 host-page 路由里挑定。
安装
服务端 SDK(Maven)
SDK 是 Spring Boot Starter,加依赖即自动装配,无需手写 @Configuration。它同时发布两套制品覆盖新旧技术栈——artifactId 相同、版本坐标不同:
xml
<dependency>
<groupId>cn.atkon</groupId>
<artifactId>atkonoffice-sdk</artifactId>
<version><!-- 取 Maven Central 上的最新发布版本 --></version>
</dependency>xml
<dependency>
<groupId>cn.atkon</groupId>
<artifactId>atkonoffice-sdk</artifactId>
<version><!-- 最新版本 -->-javax</version>
</dependency>两套制品 API 完全一致,区别仅在底层依赖
javax.*还是jakarta.*的 Servlet / WebSocket 命名空间。按目标 Spring Boot 大版本二选一即可。
两项前置,缺一会「静默不工作」:
最低版本是硬要求:Spring Boot ≥ 2.7 / JDK 8(
-javax版)或 Spring Boot 3.x / JDK 17(无尾缀版)。Spring Boot 2.6 及以下不支持——SDK 采用 Spring Boot 2.7 引入的自动装配注册方式,在更低版本上应用可正常启动、零报错零警告,但自动装配不会生效、端点一个都不会注册。自查判据:启动日志应出现Mapping servlet: 'atkonofficeRpcServlet'一类的映射记录(措辞随 Spring Boot 版本可能略有差异),没有这行即自动装配未生效。WebSocket 能力由应用自带:业务页接收
documentSaved等实时事件依赖 WebSocket,请在应用里加一条依赖(SDK 不传递它):xml<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>缺它时不报任何错:打开 / 编辑 / 保存全部正常,但业务页收不到任何生命周期事件——
documentSaved不到达,闭环的最后一步是断的。
浏览器端 SDK(npm)
bash
npm install @atkonoffice/sdkjs
import atkonoffice from '@atkonoffice/sdk'四步跑通闭环
1. 后端:告诉 SDK 文档字节在哪
若文档以「一 docId 一文件」放在本地目录,只配一行即可用 SDK 内置的本地盘解析器,无需写代码:
yaml
atkonoffice:
storage:
local:
root: /var/atkonoffice/docs常见单实例部署无需单独配置后端对外地址:浏览器端 SDK(下面第 4 步的
backendBase)会随打开请求上送,服务端据此推导客户端拉起链、下载 / 保存与实时通道地址。需要显式配置atkonoffice.launch.backend-origin的场景(多实例部署、经 Web VPN / 代理的特殊拓扑)及其取值要求,见参考 · 配置项与部署要求。
需要更复杂的定位(对象存储预签名 URL、数据库字节、按 docId 鉴权)时,实现一个 AtkonofficeDocumentResolver Bean 接管即可,见服务端 SDK 参考。
2. 后端:挂一个 host-page 路由(打开)
java
@GetMapping(value = "/atkonoffice/host-page", produces = MediaType.TEXT_HTML_VALUE)
public String hostPage(@RequestParam String docId, HttpServletRequest request) {
// 保存回写地址:给相对路径即可,SDK 用本次打开的有效地址补全成绝对 URL(与文档下载地址同源)
String savePath = "/atkonoffice/doc/save?docId=" + docId;
return new AtkonofficeCtrl(request)
.webOpen(docId, AtkonofficeOpenMode.NormalEdit, "张三") // 打开模式由集成方挑定
.setSaveFilePage(savePath) // 保存路由由集成方指定
.getHostPage(); // 返回完整宿主页
}保存地址传相对路径(以
/起始,可带 query)时,其 origin 由 SDK 补全——与该文档的下载地址同源,反向代理挂在子路径下、多实例带节点前缀等部署形态下前缀不会丢,你不必自己拼 origin。也可以传完整的绝对 URL(原样使用,适用于保存到另一个域的场景)。相对路径里不得出现未编码的
://:要在 query 里带一个完整地址(如?next=https://…)请百分号编码(?next=https%3A%2F%2F…)。写错会在页面渲染前就抛出指名该参数的异常,不会产出半成品页面。
3. 后端:挂一个 save 路由(保存落盘)
java
@PostMapping("/atkonoffice/doc/save")
public void save(@RequestParam String docId,
HttpServletRequest request, HttpServletResponse response) {
AtkonofficeFileSaver saver = new AtkonofficeFileSaver(request, response);
try {
byte[] bytes = saver.getFileBytes(); // 取出编辑后的文档字节
yourStorage.save(docId, bytes); // 落到自有存储
saver.setResult("{\"ok\":true}"); // 回给客户端的结果
} finally {
saver.close();
}
}⚠️ 保存失败时务必返回非 2xx 状态码(落盘异常直接抛出即可)。客户端按 HTTP 状态码判断保存成败——把失败包在 200 的统一响应体里(如
{"code":500})会被当成保存成功,用户会在字节未落盘的情况下关窗离开。
4. 前端:在业务页里拉起编辑
js
import atkonoffice from '@atkonoffice/sdk'
// backendBase 是唯一必设字段:业务后端的对外地址(不设则 open() 直接报错)。
// 前后端同源部署可直接取当前页的源;前后端分离 / 带网关前缀时改成后端实际对外地址(如 origin + '/api')。
atkonoffice.config({ backendBase: window.location.origin })
// 保存完成后业务页收到回流
atkonoffice.on('documentSaved', (e) => {
console.log('已保存', e.doc_id, e.ok)
})
document.querySelector('#edit-btn').addEventListener('click', () => {
atkonoffice.open({
pageApi: '/atkonoffice/host-page', // 指向上面第 2 步的路由
params: { docId: 'report.docx' },
})
})至此闭环成立:点按钮 → 拉起客户端 → 本机 Office 组件真编辑 → 保存回自有存储 → 业务页收到 documentSaved。
反向代理转发
本机直连后端跑通闭环后,部署到有反向代理 / 网关 / 负载均衡的环境时(生产几乎都有),需要为 SDK 的端点配转发规则。
SDK 自动挂载的端点全部在 /atkonoffice/ 一个前缀下(拉起、会话、文档取用、保存回写、授权、实时通道),所以只需两条 location:
nginx
# ① http 块:把 Upgrade 请求映射为升级连接(实时通道要用)
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
# ① http 块:TLS 若在更外层终止,须透传外层标注的协议,不能硬写 $scheme 覆盖掉
# (硬写会让后端把 https 还原成 http,签名地址与实时通道地址随之错成 ws://)
map $http_x_forwarded_proto $fwd_proto {
default $http_x_forwarded_proto;
'' $scheme;
}
server {
# ② 实时通道单列一条:放行升级头 + 放长超时。
# 用 ^~ 前缀:命中后不再尝试正则 location,实时通道不会被静态资源之类的正则规则劫持
location ^~ /atkonoffice/ws {
proxy_pass http://your-backend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $http_host; # 用 $http_host 不用 $host:后者会剥掉端口
proxy_set_header X-Forwarded-Proto $fwd_proto;
proxy_read_timeout 3600s; # 须覆盖整段编辑会话,nginx 默认 60s 会导致每分钟失联一轮
proxy_send_timeout 3600s;
access_log off; # 或换一个不记查询串的 log_format,见下方安全前置
}
# ③ 其余 /atkonoffice/ 请求(拉起、会话、文档取用、保存回写、授权):普通 HTTP 转发
location ^~ /atkonoffice/ {
proxy_pass http://your-backend;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $fwd_proto;
client_max_body_size 100m; # 保存是文档字节上传,nginx 默认 1m 会让稍大的文档保存失败
proxy_request_buffering off; # 大文档流式上传,不在反代落盘缓冲整个请求体
proxy_read_timeout 300s; # 覆盖你的 save 端点处理时长
proxy_send_timeout 300s;
}
# ④ 你自己的业务端点照常
location / {
proxy_pass http://your-backend;
}
}五个易错点,按症状对号入座:
| 配错的地方 | 症状 |
|---|---|
| 实时通道没放行升级头(缺 ②) | 打开、编辑、保存全部正常,但业务页永远收不到任何事件回推——最容易被误判成「前端事件没接对」 |
| 实时通道读超时用了默认 60s | 约每分钟失联一轮、随后自愈,最容易被当成「网络不稳定」长期带病运行 |
client_max_body_size 用了默认 1m | 小文档保存正常、稍大的文档保存失败,且失败点在反代、后端日志里什么都看不到 |
② 没写 ^~,而站点里有正则 location | 正则 location 优先于普通前缀匹配,会把实时通道请求抢走,症状同第一条 |
TLS 在更外层终止,本层却硬写了 X-Forwarded-Proto $scheme | 后端把 https 还原成 http,签发出的实时通道地址成了 ws://,浏览器按混合内容拦截 → 收不到事件回推 |
同域名 + 路径前缀部署(如整套挂在 https://doc.example.com/api 下):把上面两条 location 的路径加上你的前缀(/api/atkonoffice/ws 与 /api/atkonoffice/),前端 atkonoffice.config({ backendBase: origin + '/api' }) 与之对齐。前缀剥不剥要与后端实际路由一致:后端应用仍在根路径注册端点时,转发时用 rewrite ^/api(/.*)$ $1 break; 把前缀剥掉;后端本身就跑在该 context-path 下则原样透传。前缀只是同源下的一段路径,不改变浏览器的源,不引入跨域。
两条生产必须满足的安全前置(不是「配错会不好用」,而是做不到就不该上生产):实时通道端点的访问日志不得记录查询串(上面 ② 的 access_log off 即为此),且生产必须使用 wss / https。展开说明与完整的转发要求见参考 · 部署要求;多实例部署另有两类转发规则,见多实例部署。