WPS强制刷新文档说明.md 8.7 KB

WPS 强制刷新文档说明

问题

点击"结束编辑"后,重新打开文件,WPS 仍然显示缓存的旧版本,而不是从服务器重新获取最新文件。

原因

WPS 缓存机制

WPS 使用 fileId 作为文档的唯一标识:

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 重新获取文件:

// 生成唯一的 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 刷新:

const config = {
  appId: WPS_APP_ID,
  officeType: officeType,
  fileId: fileId,
  mount: wpsContainerRef.value,
  
  // 添加自定义参数
  customArgs: {
    timestamp: Date.now(),
    refresh: true
  }
};

说明

  • customArgs 会传递给后端回调接口
  • 后端可以根据这些参数决定是否返回新文件
  • 但 WPS 可能仍然使用缓存

方案 3:后端接口添加版本号

在后端的文件信息接口中返回版本号:

{
  "file": {
    "id": "28",
    "name": "文档.pdf",
    "version": "1735545600000",  // ← 版本号
    "download_url": "http://..."
  }
}

WPS 会根据版本号判断是否需要重新下载。

实现代码

当前实现(方案 1 + 方案 2)

// 初始化 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

后端实现建议

@GetMapping("/v1/3rd/file/info")
public R<FileInfo> 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. 使用版本号

    const fileId = `${props.document.id}_v${props.document.version}`;
    

    只有版本变化时才重新下载。

  2. 使用哈希值

    const fileId = `${props.document.id}_${props.document.hash}`;
    

    文件内容变化时哈希值才变化。

  3. 添加缓存控制

    const config = {
     // ...
     customArgs: {
       cache: props.document.allowCache ? 'true' : 'false'
     }
    };
    

常见问题

Q1: 为什么要添加时间戳?

:让每次打开的 fileId 都不同,强制 WPS 重新从服务器获取文件。

Q2: 时间戳会影响后端接口吗?

:会,后端需要解析 fileId,提取真实的文档 ID。

Q3: 可以使用其他方式吗?

:可以,比如版本号、哈希值、随机数等,只要保证每次不同即可。

Q4: 会增加服务器负载吗?

:会,因为每次都需要下载文件。如果文件很大,建议使用版本号方案。

Q5: 旧的 fileId 会被清理吗?

:WPS 服务器会定期清理过期的文档缓存。

Q6: 可以手动清理缓存吗?

:可以,调用 destroy() 方法会清理前端实例,但 WPS 服务器的缓存由 WPS 管理。

总结

核心改动

// 优化前
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 会从你的服务器重新获取最新文件,而不是使用缓存的旧版本!