# WPS 强制刷新文档说明 ## 问题 点击"结束编辑"后,重新打开文件,WPS 仍然显示缓存的旧版本,而不是从服务器重新获取最新文件。 ## 原因 ### WPS 缓存机制 WPS 使用 `fileId` 作为文档的唯一标识: ```typescript const config = { appId: 'SX20251229FLIAPD', fileId: '28', // ← 文档 ID officeType: 'f' }; ``` **问题**: - 相同的 `fileId` 会使用缓存的文档 - 即使调用 `destroy()` 销毁实例 - WPS 服务器仍然保留该 `fileId` 的文档 - 重新初始化时会加载缓存版本 ### 缓存流程 ``` 第一次打开: fileId: "28" ↓ WPS 从你的服务器获取文件 ↓ WPS 服务器缓存文件 ↓ 显示文档 点击"结束编辑": 调用 destroy() ↓ 销毁前端实例 ↓ 但 WPS 服务器仍保留缓存 重新打开: fileId: "28" ← 相同的 ID ↓ WPS 发现缓存存在 ↓ 直接使用缓存(不从你的服务器获取) ↓ 显示旧版本 ❌ ``` ## 解决方案 ### 方案 1:添加时间戳到 fileId(推荐) 每次初始化时使用不同的 `fileId`,强制 WPS 重新获取文件: ```typescript // 生成唯一的 fileId const timestamp = Date.now(); const fileId = `${props.document.id}_${timestamp}`; const config = { appId: WPS_APP_ID, officeType: officeType, fileId: fileId, // ← 每次都不同 mount: wpsContainerRef.value }; ``` **优势**: - ✅ 每次打开都是新的 `fileId` - ✅ WPS 会重新从服务器获取文件 - ✅ 不会使用缓存 - ✅ 简单可靠 **示例**: ``` 第一次打开:fileId = "28_1735545600000" 第二次打开:fileId = "28_1735545700000" 第三次打开:fileId = "28_1735545800000" ``` ### 方案 2:添加 customArgs 参数 在配置中添加自定义参数,提示 WPS 刷新: ```typescript const config = { appId: WPS_APP_ID, officeType: officeType, fileId: fileId, mount: wpsContainerRef.value, // 添加自定义参数 customArgs: { timestamp: Date.now(), refresh: true } }; ``` **说明**: - `customArgs` 会传递给后端回调接口 - 后端可以根据这些参数决定是否返回新文件 - 但 WPS 可能仍然使用缓存 ### 方案 3:后端接口添加版本号 在后端的文件信息接口中返回版本号: ```json { "file": { "id": "28", "name": "文档.pdf", "version": "1735545600000", // ← 版本号 "download_url": "http://..." } } ``` WPS 会根据版本号判断是否需要重新下载。 ## 实现代码 ### 当前实现(方案 1 + 方案 2) ```typescript // 初始化 WPS 编辑器 const initWpsEditor = async () => { if (!wpsContainerRef.value || !props.document?.ossId) { return; } try { wpsLoading.value = true; wpsError.value = ''; const WebOfficeSDK = (window as any).WebOfficeSDK; const officeType = getFileType(props.document.fileName || ''); // 生成唯一的 fileId(添加时间戳) const timestamp = Date.now(); const fileId = `${props.document.id}_${timestamp}`; console.log('[WPS] 初始化配置:', { appId: WPS_APP_ID, officeType: officeType, fileId: fileId, fileName: props.document.fileName, timestamp: timestamp }); // 初始化配置 const config = { appId: WPS_APP_ID, officeType: officeType, fileId: fileId, // ← 每次都不同 mount: wpsContainerRef.value, // 添加自定义参数 customArgs: { timestamp: timestamp, refresh: true } }; // 初始化编辑器 wpsInstance = WebOfficeSDK.init(config); wpsLoading.value = false; console.log('[WPS] 编辑器初始化成功'); } catch (err) { console.error('[WPS] 初始化失败:', err); wpsError.value = err.message || '初始化失败'; wpsLoading.value = false; } }; ``` ## 工作流程 ### 优化前 ``` 第一次打开: fileId: "28" ↓ WPS 从服务器获取文件 ↓ 显示文档 结束编辑: destroy() ↓ 销毁实例 重新打开: fileId: "28" ← 相同 ↓ WPS 使用缓存 ❌ ↓ 显示旧版本 ``` ### 优化后 ``` 第一次打开: fileId: "28_1735545600000" ↓ WPS 从服务器获取文件 ↓ 显示文档 结束编辑: destroy() ↓ 销毁实例 重新打开: fileId: "28_1735545700000" ← 不同 ↓ WPS 重新从服务器获取 ✅ ↓ 显示最新版本 ``` ## 后端接口影响 ### 文件信息接口 WPS 会调用你的后端接口获取文件信息: ``` GET /v1/3rd/file/info?_w_appid=xxx&_w_fileid=28_1735545600000 ``` **注意**: - `_w_fileid` 参数现在包含时间戳 - 后端需要解析 `fileId`,提取真实的文档 ID **后端实现建议**: ```java @GetMapping("/v1/3rd/file/info") public R getFileInfo(@RequestParam("_w_fileid") String fileId) { // 解析 fileId,提取真实的文档 ID String realDocId = fileId.split("_")[0]; // "28_1735545600000" -> "28" // 根据真实 ID 获取文档信息 Document doc = documentService.getById(realDocId); // 返回文件信息 return R.ok(buildFileInfo(doc)); } ``` ### 文件下载接口 ``` GET /resource/oss/downloadWithoutPermission/{ossId} ``` **不受影响**: - 下载接口使用 `ossId`,不是 `fileId` - 无需修改 ## 调试日志 ### 优化前 ``` [WPS] 初始化配置: { appId: "SX20251229FLIAPD", officeType: "f", fileId: "28", fileName: "文档.pdf" } ``` ### 优化后 ``` [WPS] 初始化配置: { appId: "SX20251229FLIAPD", officeType: "f", fileId: "28_1735545600000", fileName: "文档.pdf", timestamp: 1735545600000 } ``` ## 测试场景 ### 场景 1:正常打开 ``` 操作:打开文档 预期:fileId = "28_[当前时间戳]" 结果:WPS 从服务器获取文件 ``` ### 场景 2:结束编辑后重新打开 ``` 操作: 1. 打开文档(fileId = "28_1735545600000") 2. 点击"结束编辑" 3. 重新打开文档(fileId = "28_1735545700000") 预期:两次的 fileId 不同 结果:WPS 重新从服务器获取文件 ``` ### 场景 3:快速重复打开 ``` 操作: 1. 打开文档 2. 立即关闭 3. 立即重新打开 预期:两次的 fileId 不同(时间戳不同) 结果:WPS 重新从服务器获取文件 ``` ### 场景 4:同一文档多次打开 ``` 操作: 1. 打开文档 A(fileId = "28_1735545600000") 2. 关闭 3. 打开文档 B(fileId = "29_1735545700000") 4. 关闭 5. 再次打开文档 A(fileId = "28_1735545800000") 预期:每次的 fileId 都不同 结果:每次都从服务器获取最新文件 ``` ## 性能考虑 ### 优势 - ✅ 每次都获取最新文件 - ✅ 不会显示旧版本 - ✅ 用户体验好 ### 劣势 - ⚠️ 每次都需要下载文件(不使用缓存) - ⚠️ 增加服务器负载 - ⚠️ 增加网络流量 ### 优化建议 如果文件很大,可以考虑: 1. **使用版本号**: ```typescript const fileId = `${props.document.id}_v${props.document.version}`; ``` 只有版本变化时才重新下载。 2. **使用哈希值**: ```typescript const fileId = `${props.document.id}_${props.document.hash}`; ``` 文件内容变化时哈希值才变化。 3. **添加缓存控制**: ```typescript const config = { // ... customArgs: { cache: props.document.allowCache ? 'true' : 'false' } }; ``` ## 常见问题 ### Q1: 为什么要添加时间戳? **答**:让每次打开的 `fileId` 都不同,强制 WPS 重新从服务器获取文件。 ### Q2: 时间戳会影响后端接口吗? **答**:会,后端需要解析 `fileId`,提取真实的文档 ID。 ### Q3: 可以使用其他方式吗? **答**:可以,比如版本号、哈希值、随机数等,只要保证每次不同即可。 ### Q4: 会增加服务器负载吗? **答**:会,因为每次都需要下载文件。如果文件很大,建议使用版本号方案。 ### Q5: 旧的 fileId 会被清理吗? **答**:WPS 服务器会定期清理过期的文档缓存。 ### Q6: 可以手动清理缓存吗? **答**:可以,调用 `destroy()` 方法会清理前端实例,但 WPS 服务器的缓存由 WPS 管理。 ## 总结 ### 核心改动 ```typescript // 优化前 const fileId = `${props.document.id}`; // 优化后 const timestamp = Date.now(); const fileId = `${props.document.id}_${timestamp}`; ``` ### 效果 - ✅ 每次打开都是新的 `fileId` - ✅ WPS 重新从服务器获取文件 - ✅ 不会显示缓存的旧版本 - ✅ 用户看到的始终是最新文件 ### 后端注意事项 - ⚠️ 需要解析 `fileId`,提取真实的文档 ID - ⚠️ `fileId` 格式:`{documentId}_{timestamp}` - ⚠️ 示例:`"28_1735545600000"` → 文档 ID 是 `"28"` 现在重新打开文件时,WPS 会从你的服务器重新获取最新文件,而不是使用缓存的旧版本!