ARTICLE · 1219338
WPS 云空间不够用?我做了个插件,让本地文档自动同步到自己的服务器
WPS 云空间不够用?我做了个插件,让本地文档自动同步到自己的服务器

免费云空间不够用时,我给 WPS 做了一个个人加载项,让本地文件保存后同步到自己的服务器。
我最初想解决的事很简单:文档照常保存在电脑上,按下保存后,再多一份到我自己的服务器。打开文件时不必先登录某个网页,也不必把编辑流程搬出 WPS。
这个需求听起来像“把云盘地址换一下”。真正动手后,我发现要先回答另一个问题:WPS 里的加载项能否知道用户刚保存的本地文件,并把它安全地交给自建服务?
答案是可以做成一个个人同步插件。插件保留 WPS 现有功能,在本地保存完成后,把文件副本上传到自己控制的服务端。
先保存本地,再同步一份到服务器
这套流程从 WPS 的保存事件开始。WPS 开放平台为文字、表格和演示组件分别提供加载项事件。文字组件的 FileAfterSave 会在文件保存后触发,回调参数包含文档对象、保存路径和格式。表格与演示组件也提供保存后事件。
项目按组件分别注册事件:ET 监听工作表变化,WPP 监听选区变化,Writer 监听文档和内容变化。
本地文件确认可用后,加载项向自建服务申请一个短时上传票据,再调用 WPS 的 OAAssist.UploadFile 从本地路径直接上传。
这意味着日常编辑仍然发生在 WPS 里。插件没有把文档改存到临时目录,也没有要求用户从网页打开文档。原文件留在原来的位置,服务器收到的是一份云端副本。

WPS 文档列出了保存后事件,也列出了 OAAssist.UploadFile(FileName, FilePath, Url, Field, Success, Fail) 这个上传接口。插件把事件和上传接口接在一起,就能在用户保存本地文档后发起上传。实际使用仍要按组件和 WPS 版本验证事件是否正常触发。
下面这段展示加载项怎样把本地文件路径和短时上传地址交给 WPS,并处理上传回调。为适合公众号阅读,省略了外层 Promise 与异常捕获;这是删节摘录:
const accepted = Application.OAAssist.UploadFile( name, filePath, uploadUrl,'file',() =>resolve(),(error) => {const message = typeof error === 'string' ? error : JSON.stringify(error || {});const failure = newError(message || 'WPS 文件上传失败');if (/409|conflict/i.test(message)) failure.code = 'version_conflict';reject(failure); },);if (accepted === false) reject(newError('WPS 拒绝启动文件上传'));回调负责报告上传成功或失败;accepted === false 则表示 WPS 没有接受上传任务。省略的 Promise 和外层异常捕获负责把结果交给插件其余逻辑。
服务器会把上传内容保存为新的云端版本并更新文件摘要。本地原文件仍留在用户选择的位置。
服务器怎么认出“这是同一个文件”
只按文件名识别会出问题。电脑上可能有好几个都叫“周报.docx”的文件,它们应该对应不同的云端文档。
所以插件用“本机标识 + 本地路径摘要”作为映射键。完整路径只留在客户端,用 SHA-256 算出摘要后再提交给服务端。服务端据此把本地文件映射到一个独立的云端 fileId。文件名用于展示,不承担唯一标识的作用。
客户端先把规范化后的本地路径做摘要,并读取这条路径对应的本机标识和云端映射:
const pathHash = awaitsha256(filePath.toLowerCase());const machineId = getMachineId();const fileMap = readJson(MAP_KEY, {});const known = fileMap[pathHash] || {};申请上传票据时,发送的是 pathHash 和 machineId,不是电脑上的完整目录路径。
服务端把本机标识和路径摘要组合成映射键,首次同步时建立绑定,以后据此找到原来的云端文件。
这里有一个隐私边界需要说清楚:服务端收不到完整的本地目录路径,但会收到文件名、路径摘要和本机随机标识。路径摘要仍然是稳定的映射信息,不能把它理解为匿名化或加密。
上传失败时,本地文件仍然在
同步是保存后的附加动作。网络断开、服务器不可用或令牌错误时,本地保存结果不会回滚或删除。待处理文件路径会留在加载项的本地队列中;普通网络错误按 1.5 秒、5 秒、15 秒重试,版本冲突则停止自动重试。
云端更新还会在票据申请和文件写入时核对版本。如果另一台设备先上传了新版本,服务端拒绝覆盖并报告冲突。它不会自动合并两份文档;出现冲突后仍需要用户决定保留哪一份。
Word、Excel、PPT 不能共用同一套变化事件
三类文件都能监听保存,但编辑过程中用来触发空闲保存的事件并不相同。Writer 使用文档变化事件,ET 使用工作表变化事件。WPP 的公开事件表没有内容变化事件,项目用选区变化作为近似信号,再检查演示文稿是否处于未保存状态。
因此,演示文稿的“停止编辑一段时间后自动保存”属于尽力适配。选区变化不等于每一次内容修改,不能保证捕获所有编辑动作。用户主动保存后的同步路径更直接,空闲自动保存的覆盖范围则需要按 WPS 版本继续验证。
加载项收到变化事件后会重置计时器;默认等待 5 秒,用户可以调整等待时间:
if (previous) clearTimeout(previous.timer);const timer = setTimeout(() => {autoSaveAndSyncIfDifferent(doc, filePath);}, delaySeconds * 1000);idleSaveTimers.set(key, { timer, doc });摘录省略了计时器初始化和等待状态提示;如果用户在计时结束前继续编辑,旧计时器会被清除并重新开始。
计时结束后,插件检查文档是否未保存;确认有修改后调用 Save(),再把文件加入上传队列。WPP 还会检查 Saved 状态,无法确认时跳过保存,避免误存。
项目把三种 WPS 组件做成三个注册入口,共用一套界面和同步逻辑。这样可以分别适配各组件的事件差异,又不用维护三份功能代码。当前设置里,“保存后自动同步”和“空闲后自动保存并同步”也是两个独立开关。
这套方案的边界
它适合希望留一份副本到自有服务器的个人用户。服务器可以部署在自己的 Linux 主机上,文件数据由服务端管理;将来存储层也可以接 NAS 或 S3 兼容对象存储。当前版本聚焦单人、多设备的文件版本同步。
同步由 WPS 加载项接收事件并上传,因此需要在 WPS 运行时安装、启用并加载。WPS 关闭或加载项未启动时,不会有保存事件交给它处理。操作系统级后台监控、多人实时协同和恢复到原始本地路径的功能都不在首版范围内。
项目已为 Writer、ET、WPP 准备了不同的组件入口,并按各自事件模型做适配。个人部署时还要配置服务端地址和访问令牌;公网使用应通过 HTTPS 保护通信。具体安装步骤和服务端配置放在项目 README 与技术方案文档中。

回到最开始的问题:按下 Ctrl+S 后,WPS 文档能不能进自己的云盘?可以通过一个运行在 WPS 内的加载项,在本地保存完成后上传到自建服务。它保留本地文件作为编辑时使用的文件,再由服务端保存云端版本。
它保留 WPS 原有的本地保存流程,并在保存完成后增加一个由自己控制的上传步骤。
主要资料
- WPS 文字事件:FileAfterSave[1]
- WPS 表格事件[2]
- WPS 演示事件[3]
- WPS OAAssist.UploadFile[4]
项目源码
WPS 自托管云同步项目源码[5]
参考链接
- WPS 文字事件:FileAfterSave: https://open.wps.cn/documents/app-integration-dev/wps365/client/wpsoffice/jsapi/addin-api/event/wps-event
- WPS 表格事件: https://open.wps.cn/documents/app-integration-dev/wps365/client/wpsoffice/jsapi/addin-api/event/et-event
- WPS 演示事件: https://open.wps.cn/documents/app-integration-dev/wps365/client/wpsoffice/jsapi/addin-api/event/wpp-event
- WPS OAAssist.UploadFile: https://open.wps.cn/documents/app-integration-dev/wps365/client/wpsoffice/jsapi/addin-api/OAAssist/member/UploadFile
- WPS 自托管云同步项目源码: https://github.com/yeffky/wps-cloud















































































