
1. 拆开这个组合kkFileView 负责调度LibreOffice 负责啃硬骨头1.1 一个被反复提起的需求场景做企业级后台的人大概率都遇到过这么一类需求业务系统里挂着一堆附件Word、Excel、PPT、PDF、TXT 混在一起产品经理一句点一下能直接看别让人下载就把一个看起来简单、做起来全是坑的活儿丢过来了。前端同学第一反应是用浏览器原生能力结果发现自己写了半天的 iframe 在 Chrome 里只会下载不会渲染后端同学想自己调 Office 的转换接口又发现服务器上根本没有 Office。kkFileView 就是为这个场景长出来的东西。它是一个开源的文件在线预览服务自带 Web 界面和 HTTP 接口业务系统只需要把文件地址按约定拼进参数里就能拿到一个可以直接嵌 iframe 的预览页。而它背后最核心的那台发动机就是LibreOffice——所有 Office 格式的解析、排版、转 PDF绝大多数都要经它一手。我最早接触这套组合是在一个文档管理系统里那时候附件预览是自己写的用了一堆第三方库硬拼docx 用 mammoth 转 HTMLxlsx 用 sheetjs 前端渲染PPT 直接放弃治疗。结果是格式稍微复杂一点就跑版表格里的合并单元格错位PPT 完全没法看。后来换成 kkFileView LibreOffice虽然也不是银弹但至少能看这个底线守住了。这篇文章想聊的不是官网那几句功能介绍而是从部署到上线这一段我自己踩过的路LibreOffice 在 Linux 上到底怎么装才不出乱码kkFileView 哪些配置不改一定会出事加水印是怎么实现的、对哪些格式无效以及那句让人一脸问号的你尝试预览的文件可能对你的计算机有害到底是什么玩意。如果你正准备把这套东西塞进生产环境或者已经装好了但预览是空白、乱码、只有一页下面的内容应该能帮你少熬两个晚上。1.2 kkFileView 的能力边界与工作原理先把它的工作链路说清楚不然后面排查问题会没有方向。kkFileView 本质是一个 Spring Boot 应用对外提供两种入口一个是它的 Web 页面带文件列表和预览区另一个是纯粹的预览接口比如http://你的地址:8012/onlinePreview?urlxxx其中 url 参数是原始文件地址做了 Base64 编码加 URL 编码的结果。业务系统通常只用后者把预览页塞进 iframe 或者新开标签页。它对不同格式的处理路线是完全不一样的这一点非常关键文件类型处理路线是否经过 LibreOfficedoc / docx / xls / xlsx / ppt / pptx / odt 等转 PDF再由前端渲染是pdf直接前端渲染否txt / 各类代码文件转 HTML前端展示否jpg / png / gif 等图片直接输出可加水印否mp4 / mp3 等音视频直接输出流否zip / rar 等压缩包列出目录结构否html / htm直接返回源码给浏览器否这张表建议你截图存一下。后续遇到任何为什么这个格式预览效果和那个不一样的问题答案基本都在这张表里。比如水印为什么对 Word 有效、对 HTML 无效看清路线就明白了——水印是在图片或 PDF 渲染链路里叠加的HTML 走的是完全另一条路压根不经过那一层。还有一点容易被忽略kkFileView 本身不做文件存储它只做转换和渲染。文件从哪来从你传给它的 url 参数里来可以是 HTTP 地址可以是本地路径也可以是 FTP。这意味着两件事一是它对源文件服务器有网络依赖源站挂了预览也就挂了二是它的 url 参数本质上是一个可以指向任意地址的入口安全配置不做就是一个标准的服务端请求伪造漏洞这个后面单独讲。1.3 LibreOffice 在里面扮演的角色LibreOffice 在这个组合里的定位很纯粹把 Office 二进制格式翻译成 PDF。它本身是一个完整的办公套件但服务器上我们用到的只是它的 headless 模式也就是无界面命令行转换能力核心命令就是 soffice。为什么非得是它因为 Office 的格式尤其是 docx、xlsx、pptx 这些 OOXML 格式排版规则极其复杂页边距、分页、表格跨页、字体回退、图形定位任何一项实现不到位转出来的 PDF 就是错位的。市面上能把这些格式还原得比较像样的开源方案基本只有 LibreOffice 一家。其他的要么是收费的商业组件要么是只做简单解析、排版还原度堪忧的库。所以你会看到一个很典型的现象kkFileView 预览效果好不好八成取决于 LibreOffice 装得好不好。中文乱码是字体问题表格错位是版本问题转换卡死是进程管理问题转换超时是并发问题。kkFileView 只是把请求转发给了 soffice真正干活的还是它。理解了这一层后面的排查思路就清晰了预览出问题先做判断——是 kkFileView 的调度层出问题还是 LibreOffice 的转换层出问题。判断方法很简单把源文件拷到服务器上手动执行一次 soffice 转换命令看输出的 PDF 对不对。这一步能省掉大量无头苍蝇式的排查。2. 动手前的选型部署形态、版本与替代方案2.1 三种常见部署形态怎么选在动手装之前先想清楚你要哪种形态因为这直接决定了后面的配置复杂度。第一种是独立部署。kkFileView 单独跑在一台机器上业务系统通过 HTTP 调用它的预览接口。这是最常见的做法好处是隔离干净升级、重启都不影响业务服务坏处是多了一跳网络调用预览接口的 url 参数得是业务系统能对外暴露的地址。我自己的项目基本都是这种一台 4 核 8G 的机器能扛住日常几百人使用。第二种是内嵌到业务服务里。把 kkFileView 的代码作为依赖拉进自己的 Spring Boot 项目作为一个模块启动。好处是没有额外机器调用是本地方法级别的坏处是它依赖的 LibfreeOffice 进程会和你的业务进程共享资源一旦转换把 CPU 吃满业务接口也跟着卡。而且它的配置项会和你自己的配置项混在一起容易冲突。除非你的预览量非常小否则我不建议这么干。第三种是容器化部署。用 Docker 跑官方有镜像但要注意镜像里默认带的那套字体通常不全中文文档照样乱码。真正要上容器建议自己基于官方镜像加一层把中文字体 COPY 进去重新构建。这个后面会讲具体做法。选哪种没有标准答案看你的团队习惯。但有一条是共通的不管哪种形态只要涉及 Office 转换LibreOffice 的版本和字体都得单独管理不能指望装上了就行。2.2 LibreOffice 版本与 kkFileView 版本的搭配版本这块我踩过坑值得说细一点。kkFileView 从 3.x 到 4.x底层的转换组件换过一次实现。早期版本用的是 jodconverter 的老版本通过 socket 连接一个常驻的 soffice 进程配置项里要填office.port默认 2002。4.x 之后改成了 jodconverter 的新版本通常使用本地进程模式通过office.home指定 LibreOffice 的安装目录由它自己去拉起和复用进程。这两个版本的配置方式完全不同你在网上搜到的教程大概率不匹配你手上的版本这是新手最容易踩的坑之一。所以第一件事确认你的 kkFileView 是哪个版本然后严格按对应版本的官方文档配。LibreOffice 这边社区里被提到比较多的稳定版本是 7.4.x 系列比如 7.4.7。这个版本在 Linux 上的 headless 转换比较稳对新版 docx 的支持也够用。不建议盲目上最新的 24.x、25.x一方面是某些发行版的字体渲染行为有变化可能导致原本正常的文档突然跑版另一方面是新版本的依赖要求可能和你的系统不匹配装起来费劲。我的建议是先确定 kkFileView 版本再对照它文档里推荐的 LibreOffice 版本区间选一个中间值不要用最新也不用太老。如果实在拿不准7.4.x 或 7.5.x 是相对安全的选择。装完之后一定要跑几个真实业务文档做回归尤其是带复杂表格、艺术字、公式的那些这些才是最容易出问题的。另外提一句 LibreOffice Online。有人会问能不能搞在线协同编辑那走的是另一条路线需要单独部署一整套在线办公服务架构复杂度、资源开销和运维成本都高一个量级而且社区版和企业版的差异也比较大。如果你的需求只是看一眼kkFileView LibreOffice 这套组合的性价比高得多。2.3 横向对比几种预览路线谁更适合你为了让你心里有底我把常见的几条技术路线拉出来对比一下。方案覆盖格式排版还原度部署成本适用场景纯前端解析docx-preview、sheetjs 等只能覆盖少数格式一般复杂排版容易崩极低不用后端前端展示简单文档格式可控kkFileView LibreOffice覆盖主流办公格式较好取决于字体和版本中等需要一台服务器企业内网文档预览最常见客户端本地预览如基于 Qt 的 PDF 组件PDF 为主高本地渲染需要装客户端桌面软件内嵌预览商业文档组件覆盖广高需要授权费用对还原度要求极高的场景在线办公服务套件覆盖广支持编辑高高架构复杂需要协同编辑的场景看这张表你会发现如果只是在网页上把附件看一眼且格式五花八门、用户量不算特别大kkFileView LibreOffice 基本是性价比最高的。如果你的文档格式很单一比如全是 PDF那根本不需要这套东西直接用浏览器自带的 PDF 预览就行别为了用而用。如果格式全是 docx 且排版简单纯前端方案也不是不能考虑好处是省了一台服务器坏处是遇到复杂文档就露馅而且前端解析大文件会直接把浏览器卡死。我个人的判断标准是只要出现用户上传的文档格式不可控这个前提就老老实实上服务端转换。3. Linux 上把 LibreOffice 装成一匹能拉车的马3.1 安装方式与依赖补齐安装 LibreOffice 有好几条路每条路的坑不太一样。走包管理器是省事的做法比如在常见的企业级 Linux 发行版上直接通过系统包管理器安装。好处是依赖自动解决坏处是仓库里的版本往往偏旧而且安装的是完整办公套件包括了图形界面相关的一大堆包。服务器上其实用不到这些但装了就装了占点磁盘空间问题不大。如果你对版本没要求这条路最省心。走官方压缩包是可控的做法。从官网下载 deb 或 rpm 的压缩包解压到指定目录比如/opt/libreoffice7.4然后手动处理依赖。这条路的好处是版本完全可控卸载的时候直接删目录不污染系统。坏处是要手动补依赖常见的缺失库包括字体渲染、图形、压缩相关的若干系统库缺了就会在转换时报错。排查方法很简单直接跑一次转换命令看报什么错缺什么补什么。不管走哪条路有一个操作一定要做确认 soffice 可执行文件的路径并且用一个普通用户不要用 root测试转换。LibreOffice 在 root 下运行会因为用户配置目录权限问题报奇怪的错而且以 root 跑一个大体积的文档解析进程本身也不安全。正确做法是建一个专用的低权限账号比如就叫 office专门用来跑转换。测试命令大概长这样# 切换到专用账号执行一次静默转换 su - office -c /opt/libreoffice7.4/program/soffice \ --headless --invisible --norestore --nolockcheck --nodefault \ --convert-to pdf --outdir /tmp/out /tmp/test.docx参数里的几个开关都有讲究。--headless是无界面模式服务器上必须加--invisible让窗口不显示--norestore禁止崩溃恢复对话框不加这个在某些异常场景下进程会卡住等交互--nolockcheck跳过锁文件检查避免多进程互相干扰时误判--nodefault不加载默认文档能加快启动速度。跑完去看/tmp/out下有没有生成同名 PDF。有说明基础转换通了没有看命令行输出通常是缺依赖或字体。提示第一次执行转换时 LibreOffice 会在用户目录下创建配置文件夹这个过程比较慢可能十几秒。别以为是卡死了等一下再看。3.2 中文字体与语言配置乱码的根源在这十个中文乱码问题九个是字体没装。这句话我可以说得非常肯定。LibreOffice 转换时如果找不到文档里指定的字体会做字体替换替换结果通常是一个不含中文字形的西文字体于是所有汉字变成方块或者干脆消失。服务器环境特别是精简安装的系统镜像默认只带很少的西文字体中文字体一个没有所以转换出来的 PDF 必然乱码。解决办法是装中文字体。做法很简单把字体文件TTF 或 TTC 格式拷贝到系统的字体目录通常是/usr/share/fonts/下面新建一个子目录然后执行缓存刷新命令# 拷贝字体后刷新字体缓存 mkdir -p /usr/share/fonts/chinese cp *.ttf /usr/share/fonts/chinese/ chmod 644 /usr/share/fonts/chinese/* fc-cache -fv # 验证中文字体是否被识别 fc-list :langzh最后那条fc-list :langzh是关键验证步骤输出为空就说明字体没生效需要检查目录结构或者文件权限。实际生产环境建议装的字体一套常用的黑体、一套宋体、一套楷体基本够用另外把办公文档里高频出现的那几种字体都覆盖上。如果你们业务文档里有固定的模板字体比如某个企业内部标准字体那必须把那个字体也装上否则模板文档转出来一定是错的。这里有个特别容易被忽视的点字体装完之后要重启 kkFileView 和 LibreOffice 进程。因为字体缓存是在进程启动时加载的你在运行期间装了字体已有进程不会感知到看起来就像明明装了还是乱码。我见过不止一个同事在这上面卡了半天。至于界面语言设置成中文这件事服务器 headless 模式下其实意义不大因为你根本看不到界面。它主要影响的是文档默认语言属性对转换结果影响微乎其微。如果确实需要设置可以通过命令行参数指定语言或者在用户配置目录里改配置但说实话在服务器场景下把精力花在字体上比花在界面语言上回报高得多。3.3 headless 转换验证与常驻进程管理手动转换通了之后第二件事是把 ips 常驻进程管理好。LibreOffice 每次启动都要加载一大堆组件冷启动几秒到十几秒不等。如果每次预览请求都新起一个进程性能直接崩掉。所以实际部署时一定要让它常驻由 kkFileView 的转换组件管理进程池。这里有个必须知道的坑单个 soffice 进程的并发转换能力很弱。它内部是有状态的设计多个转换请求同时打进来轻则排队重则直接卡死或者崩溃。所以生产环境通常要起多个实例配置多个端口让转换组件在多个进程之间轮询。在 kkFileView 的配置里通常能看到类似office.port或office.ports的项可以填多个端口号用逗号分隔。每个端口对应一个独立的 soffice 进程。数量怎么定我的经验是按 CPU 核数的一半左右来配比如 8 核机器配 3 到 4 个。太多会互相抢资源反而更慢太少则并发上不去用户排队等待。进程还有个绕不开的问题长时间运行后可能假死。表现为端口还在监听但转换请求没有任何响应一直挂着。这种情况在早期版本里出现频率不低。应对手段是在 kkFileView 里配置任务超时比如 30 秒没结果就中断同时在操作系统层面加一个定时任务在业务低峰期比如凌晨把 soffice 进程全部重启一遍。虽然粗暴但非常有效。超时配置的思路也要说一下。默认的超时值对一些超大文档是不够的比如一个几百页带大量图片的 PPT转 PDF 可能要一两分钟。超时设太短用户看到的是转换失败设太长一旦进程假死请求会长时间占着线程不释放把整个服务拖垮。我的做法是设一个相对宽松但有限度的值比如 120 秒然后在前端把超时状态明确展示出来不要让用户对着白屏干等。4. kkFileView 的关键配置与实用玩法4.1 必须改的几个配置项默认配置能跑起来但默认配置直接上生产几乎一定会出事。下面这几项我每次部署都会改。服务端口和上下文路径。默认端口 8012如果和你的其他服务冲突就改掉。另外如果前面挂了反向代理要确保代理层把原始请求的协议、主机、端口正确透传否则 kkFileView 生成的回调地址会不对预览页里的资源加载会 404。相关的请求头转发配置要检查一遍。转换超时。前面提过这个值要结合你们的文档实际情况调。可以先用默认值跑一段时间看日志里有没有超时记录再决定往上调还是往下调。缓存目录。这个是重点。kkFileView 转换出来的 PDF、图片都需要落盘缓存默认目录可能在 jar 包同级或者临时目录下。临时目录的问题是系统清理策略可能会把文件删掉而且磁盘空间往往没规划。一定要显式指定到一个空间充足、单独挂载的数据盘目录上。我见过一次事故就是缓存目录所在的根分区被占满导致整个服务不可用。缓存过期清理策略。转换产物会随着时间不断累积尤其是用户反复上传新版本文件的场景。要配置定时清理比如每天凌晨清掉超过 24 小时的缓存。配置的时候注意 cron 表达式格式写错了不生效而且不会有明显报错只能靠观察磁盘增长发现。可信主机配置。这个直接关系到安全放在下一节讲。配置改完之后强烈建议写一个简单的验证清单访问一次 Office 文档、一次 PDF、一次图片、一次压缩包确认都能正常预览。四类都通了才算配置没问题。4.2 加水印的实现细节与坑水印是问得最多的功能之一因为多数场景下预览的都是内部敏感文档防止截图外泄总得做点什么。先说实现原理。水印不是简单地在页面上盖一层 div那种做法用户右键一删就没了等于没有。kkFileView 的做法是在渲染链路里叠加也就是说文件在服务端被处理时就把水印内容画进去了前端拿到的是带水印的图或者 PDF删不掉。配置项通常包括水印文字内容、字体、字号、透明度、横向间隔、纵向间隔、旋转角度这些。参数名各版本略有差异但逻辑是一样的。调整的核心是三组平衡透明度不能太低也不能太高。太低比如 0.05用户根本看不见起不到警示作用太高比如 0.5会严重影响阅读用户投诉看不清内容。我的经验值是 0.15 到 0.25 之间具体看你水印文字的颜色深浅。间隔要兼顾覆盖率和可读性。间隔太大水印集中在少数区域用户裁掉就行间隔太小满屏都是水印正文完全没法看。横向和纵向间隔我一般设成 150 到 200 像素这个量级具体根据页面尺寸调。水印内容要有指向性。只写内部资料意义不大建议带上能定位到人的信息比如当前登录用户姓名 工号或者用户 ID 时间戳。这样一来如果真的发生截图外泄你可以追溯到是谁。这个做法在很多企业内部文档系统里是标配。现在说坑。第一个坑也是最大的坑水印对部分格式无效。前面那张路由表已经说明了原因。走图片渲染链路的Office 转 PDF 再转图片、直接预览的图片能加水印走纯前端渲染链路的比如 HTML、纯文本加不了因为压根没经过叠加那一层。所以如果你的业务里有 HTML 附件别指望水印能保护它们这类文件要单独做权限控制。第二个坑动态水印需要前端配合。水印内容如果是跟着登录用户走的那它就不能写死在服务端配置里得由业务系统在调用预览接口时把水印文字作为参数传过去。参数怎么传不同版本的支持程度不一样有的是 URL 参数有的是请求头。上线前一定要拿真实链路测一遍确认传进去的参数确实生效了别配了半天还是在用默认的静态水印。第三个坑中文字体。水印文字如果用的是服务器上没有的字体同样会显示成方块。所以水印字体这一项务必设成你确认已安装的字体名称。4.3 缓存目录、转换目录与磁盘治理磁盘这件事不上生产不会觉得是个事上了生产一定是个事。一次 Office 转 PDF中间会产生临时文件PDF 转图片每页生成一张图原文件可能还会被缓存一份。一个 20 页带图的 PPT转完之后磁盘占用可能是原文件的十几倍。如果你们的业务是用户频繁上传新文件并预览磁盘增长速度会远超预期。几个治理手段组合起来用第一缓存目录独立挂载。单独一块盘只放转换产物满了也不会影响系统盘。同时给这块盘设好监控告警使用率超过 80% 就报警。第二设置合理的过期时间。缓存保留时间是权衡太短用户重复预览同一个文件会反复触发转换CPU 白烧太长磁盘压力大。我一般设 12 到 24 小时。如果你们的业务有明显的上传后集中查看特征24 小时比较合适。第三清理任务要验证。配置了清理策略不代表它真的在跑。上线后第二天一早去缓存目录看看昨天生成的文件还在不在。我就遇到过 cron 表达式写错导致一个星期没清理、磁盘差点写满的情况。第四做好单文件大小限制。上传侧做限制预览侧也要做。一个几百兆的 PPT 转 PDF能把内存吃干净而且转换时间极长用户体验也差。超过阈值的文件建议直接引导用户下载别硬转。5. 预览异常逐个拆从空白页到那句可能有害的提示5.1 HTML 与纯文本预览为什么最容易出事HTML 文件的预览是这个项目里最特殊的一块值得单独拎出来说。大多数格式都是转成另一种格式再渲染唯独 HTML 是直接把源码返回给浏览器执行。这意味着几件事。第一脚本会执行。如果你的预览服务允许用户上传 HTML 文件那么这个 HTML 里的脚本会在你的服务域名下执行。攻击者可以构造一个 HTML里面写一段脚本去读取当前域下的 Cookie 或者调用你的接口。这是个实打实的安全问题不是理论风险。所以最稳妥的做法是在允许预览 HTML 之前先想清楚你的用户上传是否可控。如果不可控直接禁掉 HTML 的预览能力让它走下载。第二样式和资源大概率加载不出来。HTML 里的图片、CSS、JS 通常是相对路径引用的预览页只拿到一个孤零零的 HTML 文件那些资源全部 404。用户看到的就是一堆没有样式的纯文字跟预期完全不一样。如果要把 HTML 预览做好得在服务端做资源内联处理把外链的资源抓下来嵌进去工程量不小。第三也是最常见的问题用户看到的不是渲染结果而是文件被下载下来了。这个现象的成因是响应头没设对。浏览器判断一个响应是渲染还是下载主要看Content-Type和Content-Disposition。如果服务端返回的Content-Type是通用的二进制类型或者Content-Disposition带了attachment浏览器就会弹下载框而不是在页面里渲染。纯文本文件也有类似的问题。如果响应头里的字符集没指定或者指定错了中文就会显示成一堆问号或方块。解决办法是在返回内容时明确指定Content-Type: text/plain; charsetUTF-8。这个字符集要和文件实际编码一致如果源文件是别的编码还得先做一次转码。注意HTML 预览这条路径既是安全问题的高发区也是显示问题的高发区。如果业务上没有强需求我的建议是直接关闭 HTML 的在线预览让它走下载流程能省掉一大半麻烦。5.2 那句你尝试预览的文件可能对你的计算机有害从哪来的这句话很多人搜过搜出来的答案还都是答非所问。我把它讲清楚。这句话本身不是 kkFileView 提示的也不是 LibreOffice 提示的。它是操作系统的安全机制在打开文件时弹出的拦截提示。触发链条通常是这样的浏览器从你的预览服务拿到响应因为响应头配置的问题它没有把内容当成网页渲染而是当成了一个要保存下来的文件文件下载到本地后用户双击打开操作系统的安全机制识别到这个文件类型或者来源不可信于是弹出这句提示。所以问题的根在响应头不在系统设置。排查方向有这么几条先看预览接口返回的响应头。用浏览器开发者工具的网络面板找到那次预览请求看Content-Type是什么。如果是一堆看不懂的二进制类型基本就确定了。再看Content-Disposition如果是attachment开头那浏览器一定是下载而不是渲染。然后是检索预览 url 参数里的文件扩展名。如果扩展名是那种系统认为高危的类型即使响应头配对了浏览器也可能强制下载。这种文件的预览本身就不该做直接引导下载并提示用户注意来源即可。再就是文件来源的问题。操作系统对从网络下载的文件会打一个标记这跟文件实际内容无关。用户看到提示后如果确认来源可信是可以手动解除的。但从产品角度你不应该让用户去面对这个提示正确做法是让服务端把响应头配对让浏览器根本不下载。再有一个容易被忽视的情况如果预览服务的域名是 http 而页面是 https或者反过来浏览器会因为混合内容策略做各种奇怪的处理包括强制下载。上线前把协议统一好。5.3 常见故障速查表把我在实际运维中遇到过的问题整理成表遇到问题先对着查一遍能省不少时间。现象最可能的原因排查与解决预览页空白无任何内容文件 url 参数编码错误服务端拉不到文件检查 Base64 和 URL 编码是否做了两次手动 curl 一下源地址看是否可达Office 文档中文显示成方块服务器缺中文字体装字体后执行缓存刷新然后重启转换进程表格、排版整体错位LibreOffice 版本与文档格式不匹配换一个稳定版本用真实文档做回归测试转换请求长时间无响应soffice 进程假死或并发超过处理能力配置任务超时增加进程数定时重启进程预览只有第一页前端渲染组件加载失败或分页资源没请求到看浏览器控制台报错检查静态资源路径预览变成下载文件响应头 Content-Disposition 或 Content-Type 不对检查代理层是否改写了响应头打开文件时系统提示可能有害浏览器把响应当成下载文件处理修正响应头让浏览器走渲染路径预览服务正常但附件接口报错源文件服务器的地址业务服务访问不到检查网络策略和地址是否用了内网无法解析的域名磁盘持续增长缓存清理策略未生效检查清理任务的 cron 表达式手动清理一次大文件预览直接失败转换超时或内存不足调大超时和堆内存同时在上传侧限制文件大小这张表里预览变成下载文件和提示可能有害其实是同一个根因的两种表现都是响应头的问题一起排查就行。而中文方块和排版错位都指向 LibreOffice 环境是部署阶段就该解决掉的。6. 上生产前的稳定性与安全功课6.1 并发、进程池与超时预览服务的性能和普通 Web 服务不太一样它的瓶颈不在网络在转换。转换是 CPU 密集型操作一个进程同时只能处理一个转换任务这是硬的物理限制优化代码解决不了。所以容量规划的思路是先估算峰值并发预览数再按每个转换进程每秒大约能处理多少文档倒推需要几个进程最后看机器核数够不够。经验数据是一个中等复杂度的 Word 文档转 PDF 大概两到五秒复杂 PPT 可能十几秒到一分钟。如果峰值同时有 10 个人点预览你至少需要 4 到 5 个转换进程才不会让最后一个用户等太久。除了进程数还有几个参数要调任务超时前面说过防止僵死任务占着进程不放。转换进程的重启周期建议每天一次。Java 服务的堆内存主要是给 PDF 渲染和其他非转换操作留够空间转换本身是独立进程不占 Java 堆。连接池和线程池要确保预览接口的线程池大小和转换进程数匹配不然会出现线程都占满了在等转换的情况反而降低了吞吐。还有一个小技巧给转换任务排队加一个上限。如果瞬间涌入大量请求与其让它们全部堆在内存里不如超过上限直接返回当前繁忙请稍后再试。这比让所有请求都超时要友好得多。6.2 安全边界URL 白名单、SSRF 与文件注入安全这块是很多人在内网环境下会忽略的但只要服务对外可达就必须做。第一个是服务端请求伪造。预览接口的 url 参数本质上是让服务端去访问一个地址。如果不做限制攻击者可以构造一个指向内网其他服务的地址通过预览接口去探测内网。防护手段是配置可信主机白名单只允许访问白名单内的地址段或域名其余一律拒绝。kkFileView 的配置里通常有对应的项务必配上。第二个是文件类型限制。不是什么文件都该被预览。可执行文件、脚本文件这类预览不仅无意义还可能触发浏览器的下载保护给用户带来困扰。建议在入口处就按扩展名做白名单只放行业务真正需要的格式。第三个是敏感信息泄露。转换过程中文件的临时副本会落在服务器的磁盘上。如果缓存目录权限过宽同机器的其他用户可以读到这些文件。解决办法是把缓存目录的权限收紧只给运行服务的专用账号读写权限。第四个是 HTML 预览带来的跨站脚本风险前面已经详细讲过。如果你的用户上传可控性差直接禁掉这个格式的预览是最省事的选择。第五个是接口鉴权。预览接口如果裸奔任何人拿到文件地址就能预览等于把内部文档公开了。正确做法是业务系统生成一个带时效的签名参数kkFileView 侧或者代理层校验签名后才允许预览。这个改造稍微麻烦一点但涉及敏感文档时必须做。注意安全配置有个特点配了不一定能立刻验证出效果但不配一定会在某一天出事。上线前花半天时间把这几项过一遍非常值。6.3 监控、日志与灰度最后说监控。预览服务最容易出的三类问题都需要监控兜住。磁盘要有使用率告警缓存目录和系统盘都要看。进程要看转换进程的数量是否正常有没有进程退出后没被拉起来。耗时统计转换任务的平均耗时和超时次数这两个指标一旦恶化说明机器扛不住了或者遇到了异常文档。日志方面kkFileView 的日志级别建议在生产环境调到信息级别把每次转换的文件名、耗时、结果都打出来。这样当用户反馈这个文件预览不了时你能直接从日志里定位到那次转换发生了什么。灰度的话如果是从旧方案迁移过来建议先切一部分用户或者一部分文件类型观察一两周。转换这类东西问题往往出现在特定文档上全量上线后才发现会很难受。7. 我自己在这一套上的一些体会这套组合我用过好几个项目说几句掏心窝的话。LibreOffice 的环境比 kkFileView 的配置重要得多。我见过太多人在配置项上折腾半天最后发现是字体没装。部署流程里字体和版本这两件事应该排在最前面先把它们搞定后面的配置才有意义。别指望它完美还原。复杂排版文档转换后出现细微差异是正常的商业组件也做不到百分之百。上线前一定要和业务方对齐预期明确能看清内容和和原文档一模一样是两码事。如果业务方要求后者趁早说明别硬扛。缓存目录一定要单独规划。这一条我强调了三遍因为被坑过。一个小小的配置项出事的时候能让整个服务挂掉。进程假死是常态不是异常。与其花精力去研究为什么假死不如老老实实配好超时和定时重启。运维的智慧有时候就是接受不完美并用简单手段兜住。水印要有但不要指望它防住所有人。它能防住随手截图转发防不住真正有心的。真正的文档安全要靠权限体系和审计水印只是一层心理威慑和事后追溯线索。最后再分享一个很实用的小习惯建一个难缠文档集。每次遇到转出来有问题的文档就把它存一份到一个固定目录里。以后每次升级 LibreOffice 版本或者改配置先用这批文档跑一遍回归五分钟就能知道有没有引入新问题。这个习惯帮我省下的时间比我写这篇文章的时间多得多。