从零搭建企业文件在线预览服务
去年公司内部知识库上线的时候产品经理提了个需求所有上传的文档必须能在浏览器里直接打开不能下载到本地再查看。当时觉得这个功能挺简单结果一做就是三周踩了无数坑。今天把三种主流方案的部署过程和踩坑记录整理出来希望能帮到同样在做文件预览服务的同学。先说下背景我们的技术栈是 Spring Boot Vue3文件存储在 MinIO 上需要预览的格式主要是 PDF、Word、Excel、PPT 这几类。下面分别介绍三种方案的部署方式和实际使用体验。PDF.js —— 最轻量的 PDF 预览方案如果你只需要预览 PDF 文件PDF.js 是上手最快的方案。Mozilla 开源纯前端渲染不需要后端服务。部署方式也很简单直接用官方提供的 viewer.html 就行# 拉取 PDF.js release 包wgethttps://github.com/mozilla/pdf.js/releases/download/v4.2.67/pdfjs-4.2.67-dist.zipunzippdfjs-4.2.67-dist.zip-d/opt/pdfjs# Nginx 代理配置server{listen8080;server_name preview.internal;location /pdfjs/{alias/opt/pdfjs/;}location /files/{proxy_pass http://minio:9000/bucket/;add_header Access-Control-Allow-Origin *;}}前端调用只需要拼接 URLconstviewerUrl/pdfjs/web/viewer.html?file${encodeURIComponent(fileUrl)};window.open(viewerUrl);踩坑记录PDF.js v4.0 以后对跨域限制变严了如果你的文件服务和 viewer 不在同一个域名下必须在文件服务器端配置 CORS 头。我当时就是忘了加Access-Control-Allow-Origin控制台报了一堆PDFWorker的跨域错误排查了整整半天。这个方案的优点是轻量、稳定、渲染效果接近原生。缺点也很明显——只能处理 PDFWord 和 Excel 完全不支持。LibreOffice Headless —— 万能但沉重的转换引擎要支持 Office 全家桶最直接的思路是先把文档转成 PDF再用 PDF.js 渲染。LibreOffice 的 headless 模式就是干这个的。Docker 部署dockerrun-d\--namelibreoffice-converter\-p3001:3000\-eTZAsia/Shanghai\-v/tmp/preview:/tmp/preview\gotenberg/gotenberg:8.5.0这里用的是 Gotenberg一个封装了 LibreOffice 的 API 服务。调用方式# 把 Word 转成 PDFcurl--requestPOST\--urlhttp://localhost:3001/forms/libreoffice/convert\--formfiles/tmp/test.docx\-o/tmp/output.pdfSpring Boot 里的调用代码PostMapping(/preview)publicResponseEntitybyte[]preview(RequestParamStringfileKey){// 1. 从 MinIO 下载原始文件byte[]fileDataminioClient.getObject(bucketName,fileKey);// 2. 调用 Gotenberg 转 PDFHttpHeadersheadersnewHttpHeaders();headers.setContentType(MediaType.MULTIPART_FORM_DATA);MultiValueMapString,ObjectbodynewLinkedMultiValueMap();body.add(files,newByteArrayResource(fileData){OverridepublicStringgetFilename(){returnsource.docx;}});ResponseEntitybyte[]responserestTemplate.postForEntity(http://localhost:3001/forms/libreoffice/convert,newHttpEntity(body,headers),byte[].class);// 3. 返回 PDF 流给前端 PDF.js 渲染returnResponseEntity.ok().header(Content-Type,application/pdf).body(response.getBody());}这个方案最大的问题是资源消耗。一个 LibreOffice 进程大约吃 200-400MB 内存并发转换的时候直接 OOM。我们在生产环境限制并发数为 3超过的请求排队等待。2025 年 3 月那次线上事故就是因为市场部批量上传了 200 多个 PPT转换队列堆积导致整个服务假死GC 停顿超过 30 秒。另外复杂 Excel 的排版容易错乱合并单元格和条件格式基本都会丢。PPT 里的动画和嵌入字体也别指望能保留。OnlyOffice —— 在线编辑预览一体化方案如果预算充裕OnlyOffice 是体验最好的选择。它不仅能预览还支持在线编辑界面和桌面版 Office 几乎一致。Docker Compose 部署version:3.8services:onlyoffice:image:onlyoffice/documentserver:8.2.0container_name:onlyofficeports:-8443:443environment:-JWT_SECRETyour_jwt_secret_herevolumes:-./logs:/var/log/onlyoffice-./data:/var/www/onlyoffice/Datarestart:always启动后等待服务就绪首次启动要拉字体包大概 3-5 分钟# 检查服务是否就绪curl-khttps://localhost:8443/healthcheck# 返回 true 说明可以用了前端集成需要加载 JS SDKscriptsrchttps://preview.internal/web-apps/apps/api/documents/api.js/scriptdividplaceholder/divscriptnewDocsAPI.DocEditor(placeholder,{document:{fileType:docx,key:unique_document_key,title:测试文档.docx,url:https://your-backend/api/files/download?fileKeyxxx},documentType:word,editorConfig:{mode:view,// view 只读预览edit 可编辑lang:zh-CN},token:jwt_token_here// JWT 安全校验});/scriptOnlyOffice 的坑主要在 JWT 配置上。v7.0 之后默认开启 JWT 验证如果前后端的 secret 不一致页面会白屏且不报任何错误。我第一次部署的时候对着白屏发呆了两个小时最后翻/var/log/onlyoffice/documentserver/converter/err.log才看到 JWT 验证失败的日志。还有一个隐藏问题OnlyOffice 对文档 key 很敏感。如果同一个 key 对应的文档内容变了但 key 没更新打开的还是缓存版本。所以每次文档修改后必须生成新的 key。三种方案怎么选简单总结下我的选型建议只需要 PDF 预览 → PDF.js没有之一需要预览 Office 文档但不需要编辑 → LibreOffice PDF.js注意控制并发需要在线编辑 预览 → OnlyOffice但要准备好至少 4GB 内存的服务器如果觉得自建整套预览服务太折腾其实也可以考虑现成的方案。我们后来调研了一圈发现巴别鸟企业云盘直接支持 200 多种格式的在线预览Word、Excel、PPT、PDF、CAD 图纸、思维导图、视频、音频都能在浏览器里打开不需要本地安装任何软件。而且它有 32 个维度的权限控制可以精确到只能在线查看不能下载这种颗粒度正好满足我们产品经理的需求。专业版一年 2000 块钱包含 1T 存储不限用户数babel.cc/p/price.do比起自己维护一套预览服务的服务器成本和人力成本其实性价比挺高的。不过话说回来自建方案的好处是数据完全在自己手里适合对数据安全有强要求的企业。两种路线各有优劣看具体场景选择。FAQQ1LibreOffice 转换出现中文乱码怎么解决Docker 容器里默认没有中文字体需要手动安装。在 Dockerfile 里加一行RUN apt-get update apt-get install -y fonts-wqy-zenhei fonts-wqy-microhei fc-cache -fv或者直接把宿主机的字体目录挂载进去-v /usr/share/fonts:/usr/share/fonts。注意 Gotenberg 的镜像基于 Debian字体包名和 CentOS 不一样。Q2OnlyOffice 文档打开慢怎么优化几个方向一是开启 Redis 缓存避免每次都重新转换二是用 Nginx 做静态资源缓存字体文件和 JS SDK 都可以缓存 7 天以上三是升级到 v8.x 版本新版对大文件的加载速度优化了不少我们实测 50MB 的 PPT 打开时间从 15 秒降到了 6 秒左右。Q3PDF.js 预览大文件超过 50MB时浏览器崩溃怎么办PDF.js v4.x 支持disableAutoFetch配置不会一次性加载整个文件constloadingTaskpdfjsLib.getDocument({url:fileUrl,disableAutoFetch:true,disableStream:false});配合 Range 请求可以实现按页加载内存占用会小很多。Q4能不能用 WPS 开放平台替代 OnlyOffice可以WPS 开放平台提供了 WebOffice SDK体验和桌面端 WPS 一致。但它是 SaaS 服务文档需要上传到 WPS 的服务器或者配置私有化部署对数据合规性有要求的企业需要评估下。而且私有化部署的价格不便宜适合预算充足的中大型团队。Q5LibreOffice 转换的 PDF 和原始文档排版差异大吗Word 文档的排版还原度大约 85-90%简单的文字和表格基本没问题。主要问题是字体回退如果文档用了容器里没有的字体会用默认字体替代、复杂表格边框丢失、页眉页脚位置偏移。Excel 的问题更大一些图表和条件格式大概率会变形。如果排版还原度要求高建议直接上 OnlyOffice 或者用商业方案。