鸿蒙应用要在端内打开 Office 文档,统一版 WPS Open SDK 把入口收成 WPSApi + OpenFileRequest。专业版(ToB)与个人版(ToC)共用同一套构造函数和 sendRequest,差异在注册成功之后要不要 setWpsFileToken,而不在打开类本身再分叉。工程上真正容易散掉的是三件事:注册还没 OK 就点打开、系统选择器路径直接丢给 WPS、预览和编辑各写一套 Request。本文按分层把打开链路写清楚,并给出可放进 WpsOpen.ts 的封装。
阅读建议:已能 ohpm install @wps/wps_sdk,并且 registerApp 至少成功过一次。
为什么打开要单独成层
对接文档把调用链画成:注册 →(ToB)激活序列号 → 构造打开请求 → sendRequest。页面如果在 onClick 里同时干这四件事,日志会缠在一起。1013 是注册凭据问题,打开路径错误多半是 ResultCode.ERROR,未注册则是 Promise 异常。三层混在一个 try 里,值班同学只能猜。
| 层 | 职责 | 典型失败 |
|---|---|---|
| Gate | registerApp,ToB 再 setWpsFileToken |
1013、参数不完整 |
| Inbox | 拷贝到 filesDir |
外部 URI 权限不足 |
| Open | OpenFileRequest + sendRequest |
ERROR、未注册抛错 |
ToC 注册 OK 即可进入 Open 层,不必注入序列号。ToB 漏掉 Token 时,打开表现像客户端拒开,不要回头改 enableEdit。分支用 SdkConstants.isPersonalSdk(),不要用包名字符串猜测。
构造函数只收两件事
typescript
new OpenFileRequest(context: common.UIAbilityContext, fileUri: string)
context 来自当前 UIAbility。fileUri 建议是沙箱绝对路径或应用可读 URI。文档明确建议:从系统选择器拿到的路径先拷进本应用沙箱。WPS 是独立进程,跨沙箱读文件经常失败,且 msg 不一定友好。
Inbox 层可以很薄:
typescript
import { common } from '@kit.AbilityKit';
import fs from '@ohos.file.fs';
export function stageFile(
ctx: common.UIAbilityContext,
src: string,
name: string
): string {
const dir = `${ctx.filesDir}/wps_stage`;
fs.mkdirSync(dir, true);
const dest = `${dir}/${name}`;
fs.copyFileSync(src, dest);
return dest;
}
文件名带时间戳或业务 id,避免连续打开同一显示名互相覆盖。拷贝放在 Worker / TaskPool 还是主线程,按文件大小决定;打开按钮在拷贝完成前保持禁用。
enableEdit 是开关,不是两种 SDK
未设置或 false 都是只读。只有 true 才是可编辑。ToB 与 ToC 这条语义相同。产品把「预览」和「填写」做成两个按钮时,底层仍应是同一个 openStaged,传入不同布尔值。
OpenFileRequest.wpsToken 保留在类型上,但不推荐。ToB 在 Gate 层全局 setWpsFileToken;ToC 忽略该字段。全仓搜 req.wpsToken 应为零业务命中。水印、extraOptions、wpsTransferType 属于 Open 层之上的策略,等只读打开稳定后再加,否则 ERROR 无法归因。
sendRequest:异常不是 Result
typescript
import {
WPSApi,
OpenFileRequest,
Result,
ResultCode,
SdkConstants,
} from '@wps/wps_sdk';
export async function openStaged(
ctx: common.UIAbilityContext,
stagedPath: string,
editable: boolean
): Promise<Result> {
const req = new OpenFileRequest(ctx, stagedPath);
req.enableEdit = editable;
return WPSApi.sendRequest(req);
}
未注册成功时,这里会 throw。调用方不要写成「打开失败 code 未知」。Gate 层示例:
typescript
let ready = false;
export function bootWps(key: string, secret: string, proSn?: string): void {
WPSApi.registerApp(key, secret, {
onCallback: (r: Result) => {
if (r.code !== ResultCode.OK) {
console.error('[WPS] gate', r.code, r.msg);
return;
}
if (!SdkConstants.isPersonalSdk() && proSn) {
WPSApi.setWpsFileToken(proSn);
}
ready = true;
},
});
}
export async function openFromPicker(
ctx: common.UIAbilityContext,
pickerPath: string,
editable: boolean
): Promise<Result> {
if (!ready) {
throw new Error('WPS gate not ready');
}
const staged = stageFile(ctx, pickerPath, `${Date.now()}.docx`);
const result = await openStaged(ctx, staged, editable);
if (result.code === ResultCode.OK && !result.data) {
console.info('[WPS] launched, no transfer payload');
}
return result;
}
未开回传时 OK + 空 data 表示拉起成功。UI 文案用「已打开」,不要用「已保存」。开启 wpsTransferType 之后,再把 fileUri / transferFd 拷回本应用沙箱,那是另一篇文章的范围。
联调时怎么看日志
固定两行前缀:[WPS] gate 与 [WPS] open。1013 只出现在 gate。打开阶段再出现鉴权失败,优先怀疑 HAR 与客户端形态不匹配,而不是再改拷贝函数。调试包与正式包 bundleName 不同,凭据必须两套;ToB 序列号也要跟当前安装的 WPS 客户端核对。
建议验收顺序:
- 冷启动 gate OK
- 沙箱只读打开一份 docx
- 同一文件
editable=true - ToC 包日志不得出现
setWpsFileToken - ToB 包在 OK 回调之后必须出现注入(不要打印 SN 明文)
- 全仓无
request.wpsToken赋值
HAR 升级后:只替换依赖、clean、重跑 2 和 3,确认打开面无回归,再恢复水印与回传。不要同一周既改 Token 分支又改 enableEdit 默认值。
页面怎么绑
Ability onCreate 调 bootWps。组件 aboutToAppear 只读 ready 刷新按钮。点击走 openFromPicker。不要在列表每一项的 aboutToAppear 里构造 Request。列表页若要显示「可打开」状态,订阅 Gate 的就绪标记即可。
预览水印、禁止分享等 extraOptions 以文档标注为准,ToC 不生效的字段不要从 ToB 示例原样拷进个人版工程。统一版的价值是学习一次 OpenFileRequest,而不是忽略版本差异表。
打开封装与 Gate 不要放进同一个源文件。WpsGate.ts 只导出 bootWps 与 ready;WpsInbox.ts 只导出 stageFile;WpsOpen.ts 只导出 openStaged / openFromPicker。页面 import 打开函数,不直接 new OpenFileRequest。代码评审把「构造前 ready == true」「路径来自 stage 目录」写成条目。缺陷单固定五列:HAR 文件名、Bundle、isPersonalSdk()、注册 code/msg、打开 code/msg。五列齐了再讨论选择器 URI。
context 必须来自当前 UIAbility。把 Context 缓存在单例里,应用从后台回到前台后再打开,偶发 ERROR。扩展名与真实类型一致:xlsx 不要写成 docx。大文件拷贝放 TaskPool,完成后再使能按钮,避免用户连点两次覆盖同一目标名。Date.now() 做文件名在同一毫秒连点时仍可能冲突,业务 id 更稳。
Result.requestType 用来区分日志来源。ResultCode.NONE(-1)按异常处理。关闭回传的正整数业务码只在开了 wpsTransferType 之后出现,与「仅打开」联调分开记账。extraOptions 仅显式赋值生效,不要把文档整表默认值写进 Request。ToC 不生效的字段从 ToB 示例删掉,避免同事以为开关坏了。
小结
打开文档在统一版里不是「再学一套 API」,而是把 Gate、Inbox、Open 拆开:注册与 Token 进启动阶段,路径进沙箱函数,OpenFileRequest 只表达这次怎么打开。ToB / ToC 共用构造;默认只读;sendRequest 的 throw 与 Result.code 分列。把这三层写进 README 之后,预览和编辑只是同一个函数的布尔参数,值班日志也能按前缀分流。HAR 升级后先只替换依赖并重跑注册与只读打开,确认身份与路径无回归再恢复水印与回传。内部 Wiki 只保留一份对接文档链接,避免每人收藏不同副本。
技术交流 QQ 群:628436767。申请 HAR 与凭据时注明包名以及专业版 / 个人版需求。
基于 WPS Open SDK 鸿蒙版对接实践整理,仅供开发者参考。 官方对接文档:365.kdocs.cn/l/clQl5cek2...