"""office_proxy - 把 /office/* 请求转发到本机 univer-office Node 服务(9091)。 univer-office 承担 markdown<->IDocumentData 转换与 docx/xlsx/ppt 导出, 这些能力是 Node 生态(Univer + docx/exceljs/pptxgenjs),Python 侧无法直接实现, 故通过 ahserver 的 startswiths 机制做反向代理。 路由映射: /office/render -> http://127.0.0.1:9091/render (markdown -> IDocumentData) /office/extract -> http://127.0.0.1:9091/extract (IDocumentData -> 纯文本) /office/export/docx -> http://127.0.0.1:9091/export/docx (markdown -> docx 二进制) """ import aiohttp from aiohttp import web from appPublic.registerfunction import registerFunction from appPublic.log import error, debug OFFICE_BACKEND = 'http://127.0.0.1:9091' # hop-by-hop 头,转发时需剔除 _HOP_BY_HOP = { 'host', 'content-length', 'transfer-encoding', 'connection', 'keep-alive', 'upgrade', 'proxy-authenticate', 'proxy-authorization', 'te', 'trailers', } async def office_proxy(request, params_kw, *args, **env): """把 /office/* 转发到 9091。args = leading 之后的路径段,如 ('render',) 或 ('export','docx')。""" path = '/'.join(args) url = f'{OFFICE_BACKEND}/{path}' debug(f'office_proxy: {request.method} {url}') body = await request.read() headers = {k: v for k, v in request.headers.items() if k.lower() not in _HOP_BY_HOP} try: timeout = aiohttp.ClientTimeout(total=180) async with aiohttp.ClientSession(timeout=timeout) as session: async with session.request(request.method, url, headers=headers, data=body) as resp: resp_body = await resp.read() resp_headers = { k: v for k, v in resp.headers.items() if k.lower() not in _HOP_BY_HOP and k.lower() != 'content-encoding' } return web.Response(body=resp_body, status=resp.status, headers=resp_headers) except Exception as e: error(f'office_proxy failed: {e}') return web.Response(body=f'office backend error: {e}'.encode(), status=502) registerFunction('office_proxy', office_proxy)