实现WOPI协议时如何用服务端axios替代POST表单加载Collabora Online
问题根因
你之前的方案无法运行主要有3个核心问题:
- 直接将Collabora返回的HTML通过
srcDoc注入iframe,会导致HTML内的相对路径静态资源、后续接口请求全部指向你的前端域名,找不到对应资源 - Collabora运行依赖正确的域名上下文,直接在你的前端域名下运行Collabora的代码会触发跨域限制,后续WOPI协议相关的接口全部无法调用
- 服务端axios请求参数格式错误,没有按照原表单的要求传递
access_token表单字段,请求直接被Collabora拒绝
正确实现方案
我们采用Express做代理转发的方式实现,既满足服务端发起请求的要求,又保留iframe加载的正确上下文逻辑:
1. 服务端实现
首先安装依赖:
npm install form-data axios
路由代码如下:
const FormData = require('form-data'); const axios = require('axios'); // 辅助方法:流转字符串 const streamToString = (stream) => { const chunks = []; return new Promise((resolve, reject) => { stream.on('data', chunk => chunks.push(chunk)); stream.on('end', () => resolve(Buffer.concat(chunks).toString('utf8'))); stream.on('error', reject); }); }; routeAuthenticated( 'POST', '/MyRequest', async (req, res) => { try { const { url, token } = req.body; // 构造和原表单完全一致的form-data参数 const form = new FormData(); form.append('access_token', token); // 发起请求到Collabora const collaboraRes = await axios.post(url, form, { headers: form.getHeaders(), responseType: 'stream' }); // 透传所有响应头 Object.keys(collaboraRes.headers).forEach(key => { res.setHeader(key, collaboraRes.headers[key]); }); res.status(collaboraRes.status); // 处理HTML响应,插入base标签修正资源路径 if (collaboraRes.headers['content-type']?.includes('text/html')) { const html = await streamToString(collaboraRes.data); const collaboraOrigin = new URL(url).origin; // 插入base标签,所有相对路径资源自动指向Collabora域名 const modifiedHtml = html.replace('<head>', `<head><base href="${collaboraOrigin}/">`); res.send(modifiedHtml); return; } // 非HTML类响应直接转发流 collaboraRes.data.pipe(res); } catch (err) { console.error('代理请求失败:', err); res.status(500).send('服务端代理请求出错'); } } );
2. 客户端实现
表单组件代码
不需要修改原有iframe的逻辑,仅把表单的提交地址改为自己的Express代理接口即可:
import { useRef, useEffect } from 'react'; const CollaboraSubmitForm = (props) => { const formElem = useRef(null); useEffect(() => { formElem.current.submit(); }, []); return ( <div style={{display: 'none'}}> <form ref={formElem} action="/MyRequest" encType="multipart/form-data" method="post" target="collabora-online-viewer" id="collabora-submit-form" > <input name="url" value={props.url} type="hidden" /> <input name="token" value={props.token} type="hidden" /> <input type="submit" value="" /> </form> </div> ); }; export default CollaboraSubmitForm;
iframe组件代码
和你原有实现完全一致,不需要额外加srcDoc相关逻辑:
<iframe ref={iframeRef} title="Collabora Online Viewer" id="collabora-online-viewer" name="collabora-online-viewer" style={{ width: '100%', height: '90vh', border: 'none' }} />
注意事项
- 如果你的Collabora服务配置了自定义域名或者路径,记得调整base标签的
href值适配 - 后续WOPI接口如果也需要走代理,可以在Express新增对应的代理路由统一处理
内容的提问来源于stack exchange,提问作者mshadi
相关产品推荐
相关产品推荐

