Skip to content

开始:最小集成

本页给出一个最小可跑的「打开一份文档 → 编辑 → 保存回自有存储」闭环。跑通它,即建立了接入 ATKONOFFICE 的完整心智。更细的每个功能写法见 通用控制

接入分布在两侧

SDK坐标 / 包名运行位置职责
服务端 SDKcn.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 大版本二选一即可。

两项前置,缺一会「静默不工作」

  1. 最低版本是硬要求: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 版本可能略有差异),没有这行即自动装配未生效。

  2. 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/sdk
js
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。展开说明与完整的转发要求见参考 · 部署要求;多实例部署另有两类转发规则,见多实例部署

下一步

  • 通用控制 —— 只读打开、新建、多产物保存(含导出 PDF)、事件、界面定制等每个功能的写法
  • Word 特有功能 —— 模板填充、文档比对、修订 / 批注
  • 参考 · 配置项 —— application.yml 全表与解析器 SPI

面向集成方的产品技术文档 · 不含实现细节