HarmonyOS WPS Open SDK 实践:OpenFileRequest 打开链路与沙箱拷贝

鸿蒙应用要在端内打开 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 客户端核对。

建议验收顺序:

  1. 冷启动 gate OK
  2. 沙箱只读打开一份 docx
  3. 同一文件 editable=true
  4. ToC 包日志不得出现 setWpsFileToken
  5. ToB 包在 OK 回调之后必须出现注入(不要打印 SN 明文)
  6. 全仓无 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...

相关推荐
马剑威(威哥爱编程)1 小时前
【共创稿事节】HarmonyOS 7 Agent 开发实战 01:云端大模型 API 接入底座
华为·harmonyos
ZoeLandia2 小时前
TypeScript 01:类型注解与类型推断
前端·javascript·typescript
用户2181697049302 小时前
Kotlin 语法二
android
妖星2 小时前
android 源码分析surfaceFinger是如何通过设置属性启动bootanim
android
JoyMonitor2 小时前
APK mmap 内存增长定位:pagemap 与 DEX 结构联合定位方法
android
有什么事3 小时前
永久虚拟免费云手机能实现吗,本地安卓容器与云端托管的技术边界
android·智能手机
万物智能信息科技3 小时前
人体感应芯片PIR模块驱动开发—【万物智能之开源鸿蒙OpenHarmony系统实战开发系列教程】
驱动开发·嵌入式硬件·华为·开源·harmonyos
妖星3 小时前
Android 源码 .rc 语法详解
android
JMchen3 小时前
实战项目一——实现一个高性能图片滤镜App
android·c++·性能优化
ZoeLandia3 小时前
TypeScript 02:interface、type 与类型收窄
前端·typescript