
上周帮一个做内部知识库的团队排了个不大不小的问题他们要把系统里的 Word、Excel、PPT、PDF 甚至工程图纸直接在浏览器里打开不想让用户先下载再用本机软件看。第一反应是用浏览器原生预览能力结果 Office 系列几乎全军覆没PDF 勉强能用一套业务四种表现维护成本直接上天。最后落地的方案就是kkfileview——一个用 Java 写的开源文件在线预览服务部署完只暴露一个端口、一个接口把几十种常见格式统一转成网页能渲染的样子。本文讲的是 kkfileview 在 Linux 上的安装部署重点覆盖 CentOS 和 Debian 两条线的差异。这不是一篇复制三条命令就完事的教程因为我自己在两个发行版上各踩过一轮坑LibreOffice 版本不一致导致转换结果不同、中文字体缺失导致整页方块、进程残留导致预览一直转圈。所以下面会把为什么要装这个这个参数为什么这么配出问题怎么一步步倒推都讲清楚。适合谁看适合需要在私有环境里自建文件预览能力的后端、运维也适合只是想在自己服务器上跑一个预览服务试试水的同学。1. 先搞清楚 kkfileview 干的是哪一段活1.1 一次打开文件在它内部被拆成了三步很多人第一次接触 kkfileview会以为它是个网盘预览插件。其实它的定位很纯粹接收一个文件地址返回一个可以在浏览器里看的页面。这个过程在内部被拆成三步走。第一步是取文件。你通过 URL 参数把文件的访问地址传进来它自己去下载或者读取。注意这一步是服务端发起的也就是说 kkfileview 所在的机器必须能访问到那个文件地址这一点后面会反复提到很多预览 404的根因都在这里。第二步是转换。这一步是它真正的核心价值所在。如果目标文件是 PDF、图片、视频、音频这类浏览器原生就能渲染的格式它可以几乎零成本地直接吐出来但如果是docx、xlsx、pptx、wps、dwg这类浏览器不认识的格式就需要一个翻译器把它转成 PDF 或者 HTML。这个翻译器就是 LibreOffice早期也支持 OpenOffice现在基本都推荐 LibreOffice。第三步是渲染与缓存。转出来的 PDF 会交给前端用 PDF.js 之类的库按页渲染或者后端直接转成一张张图片返回。转好的结果通常会缓存起来同一个文件第二次打开就走缓存避免反复调用 LibreOffice 这个重量级进程。缓存策略是可配置的这是后面性能调优的主要抓手。理解了这三步你就会明白kkfileview 本身其实不重重的是它背后的 LibreOffice。所以运维上真正要操心的是 LibreOffice 的进程、字体、内存和超时而不是 Java 应用本身。1.2 什么场景值得上它什么场景别硬上先说值得上的场景。典型的是私有化部署 格式杂 用户不想装软件。比如企业内部的知识库、合同管理系统、OA 审批附件、教学资料平台。这些场景通常文件格式五花八门用户又分布在各种终端上手机上根本没有对应的阅读器服务端统一转换是最省事的路子。再说别硬上的场景这也是我经常劝退别人的地方。如果你只需要预览 PDF 和图片那完全没必要上 kkfileview浏览器iframe直接加载 PDF、img直接加载图片就够了部署一个 Java 服务的成本远高于收益。如果你的文件量级非常大、并发很高比如一天几十万次预览那要评估的就不是能不能装而是要几台机器、每台放多少个 LibreOffice 转换槽位这是完全不同的工程量级。还有一种情况是文件全部在用户本地、需要上传即预览那也得先想清楚临时目录的清理策略不然磁盘会被悄悄吃满。至于常被拿来对比的方案无非几类前端纯 JS 的文档渲染库对格式支持有限复杂排版还原度堪忧、商业化的文档转换服务效果好但要花钱、且数据要出域、自己用 LibreOffice 命令行 自研渲染层可控但开发量大。kkfileview 的定位是开箱即用、私有可控、格式覆盖够广把它当成省掉自研转换层的脚手架心态就对了。2. 装之前先把三样依赖备齐JDK、LibreOffice、中文字体2.1 JDK 版本与 JAVA_HOME 的坑第一个依赖是 JDK。kkfileview 是 Spring Boot 应用主流版本对JDK 1.8的兼容性最好用 11 或 17 大概率也能跑但如果你手上的版本比较老别冒险直接上 8。CentOS 7 上装起来很直接yum install -y java-1.8.0-openjdk java-1.8.0-openjdk-devel java -versionDebian 系则是apt update apt install -y openjdk-8-jdk # 如果源里没有 8用 11 也行装完确认下版本 java -version这里有个我踩过的坑java -version能跑不代表JAVA_HOME配对了。有些发行版的 JDK 装完不会自动设置环境变量而 LibreOffice 在某些版本下会依赖JAVA_HOME来加载 Java 相关的组件。表现是kkfileview 能启动但转换xlsx里带宏或者复杂公式的文件时会直接失败。排查方式很简单echo $JAVA_HOME # 空的话手动补上路径按实际装的位置改 export JAVA_HOME/usr/lib/jvm/java-1.8.0-openjdk export PATH$JAVA_HOME/bin:$PATH要长期生效就写进/etc/profile.d/java.sh然后source一下。这一步花两分钟能省掉后面半小时的排查。2.2 LibreOffice 为什么不能省Office 转换全靠它第二个依赖是 LibreOffice而且是必须装在 kkfileview 同一台机器上不是可选组件。原因就是前面说的翻译器角色所有 Office 系文件都得靠它把二进制格式翻译成 PDF。CentOS 上装# CentOS 7 yum install -y libreoffice libreoffice-headless libreoffice-langpack-zh-Hans # CentOS 8 / 9 Stream dnf install -y libreoffice libreoffice-headless libreoffice-langpack-zh-HansDebian 上apt install -y libreoffice libreoffice-writer libreoffice-calc \ libreoffice-impress libreoffice-java-common有两个细节值得说。第一libreoffice-headless这个包很关键。它提供的正是无界面模式服务端转换靠的就是这个模式。有些精简系统默认只装了基础包缺了 headless 的话转换会报找不到模块。第二libreoffice-java-common在 Debian 上别省它会带上一些 Java 相关的桥接组件处理 ODF 格式和部分文档时更靠谱。安装完验证一下libreoffice --version # 或者 soffice --version能打印出版本号说明命令可用。接下来要决定office.home这个配置怎么填安装方式典型路径office.home 怎么填系统包管理器安装/usr/lib/libreofficeDebian一般可留默认应用会自动探测系统包管理器安装/usr/lib64/libreofficeCentOS一般可留默认官网 tar.gz 解压/opt/libreoffice7.6必须显式写成这个目录自定义路径你放哪儿写哪儿必须显式指定我个人的建议是如果你需要控制版本就用官网 tar.gz 解压到/opt下然后在配置里显式写死office.home。为什么因为系统源里的 LibreOffice 版本往往偏老CentOS 7 自带的是 5.x对某些新版docx的排版还原会打折扣而且系统升级时版本可能被悄悄换掉转换效果就莫名其妙变了。解压式安装版本固定、升级可控出问题也容易回滚。2.3 中文字体90% 的乱码方块都出在这里第三个依赖是最容易忽略、但出问题最多的中文字体。原理很简单LibreOffice 在转换文档时需要找到文档里指定的字体来渲染文字。如果服务器上没装对应字体它只能用一个默认字体去顶而很多精简版 Linux 的默认字体不含中文字形结果就是满屏的方块或者问号。你在 Word 里看着好好的文档转成 PDF 就变成口口口根因就在这。装字体的标准流程是把 TTF/TTC 字体文件丢进系统字体目录然后刷新字体缓存。mkdir -p /usr/share/fonts/chinese # 把字体文件复制进去比如从其他机器拷贝 cp /path/to/*.ttf /usr/share/fonts/chinese/ cp /path/to/*.ttc /usr/share/fonts/chinese/ chmod 644 /usr/share/fonts/chinese/* # 安装 fontconfig精简系统可能没装 # CentOS: yum install -y fontconfig # Debian: apt install -y fontconfig # 刷新缓存 fc-cache -fv # 验证中文或西文字体已经生效 fc-list :langzh | wc -l最后那条命令会输出检测到的中文字体数量。如果结果是 0那你的乱码问题基本就跑不掉了请务必让它大于 0 再继续。如果你手头没有商用字体授权的顾虑可以直接装开源的方案Debian 系一行搞定apt install -y fonts-wqy-zenhei fonts-wqy-microhei fonts-noto-cjk这几套字体对中文的覆盖都挺全用来兜底完全够用。有个经验别只装一套字体。文档里可能指定宋体、可能指定黑体、也可能指定楷体装得越全被顶字体导致排版跑偏的概率就越低。真要追求还原度就把常见的几套中文字体都补齐。3. CentOS 上的落地流程7.9 与 8/9 差异要分开看3.1 系统层面的包与源CentOS 这条线最大的特点是版本分化严重。CentOS 7.9 和 8/9 Stream 在包管理器、默认软件版本、甚至 glibc 上都不同混着抄命令很容易卡在第一步。CentOS 7.9 用的是yum软件源里 LibreOffice 是 5.x。如果你不介意版本直接yum install -y epel-release yum install -y fontconfig java-1.8.0-openjdk libreoffice libreoffice-headlessCentOS 8 之后换成了dnf命令基本兼容dnf install -y epel-release dnf install -y fontconfig java-1.8.0-openjdk libreoffice libreoffice-headless如果你要的是新版 LibreOffice两个版本都可以走官网 tar.gz# 下载后解压到 /opt tar -zxvf LibreOffice_7.6.x_Linux_x86-64.tar.gz -C /opt/ cd /opt/libreoffice7.6/program ./soffice --version # 依赖缺的话用自带脚本补 cd /opt/libreoffice7.6 ./install --help有个细节要提醒tar.gz 版本的 LibreOffice 打包方式和系统包不一样它不一定往系统字体目录找字体但会读 fontconfig 的缓存。所以 2.3 那一步的fc-cache -fv在 tar.gz 场景下同样不能省。3.2 kkfileview 的解压、目录结构与配置改动依赖备齐后下载 kkfileview 的发行包。它官方提供两种形式tar.gz压缩包和docker镜像。这里先讲压缩包。cd /opt tar -zxvf kkfileview-4.x.x.tar.gz mv kkfileview-4.x.x kkfileview cd /opt/kkfileview ls -lh解压完你会看到一个比较规整的目录结构大致是这么几块目录 / 文件作用你是否需要动它bin/启动、停止脚本可能要改 JVM 参数config/application.properties主配置必须改lib/依赖的 jar 包一般不动log/运行日志排查问题时主要看这里file/默认的文件与缓存目录视情况改路径启动脚本里本质就是拼一条java -jar命令。关于 JVM 参数我建议一开始就改而不是等出问题再回头改# 在 bin/startup.sh 里 java 命令那一行加入 -Xms1g -Xmx2g -Dfile.encodingUTF-8 -Dsun.jnu.encodingUTF-8为什么是这两个编码参数因为文件名字里带中文、文件内容里有中文在这个场景下太常见了编码没设对轻则日志乱码重则读文件路径就报错。-Xms和-Xmx建议设成一样大比如都是 2G避免运行过程中堆反复伸缩带来的额外开销。配置文件里至少要确认这几项server.port8012 base.urlhttp://你的服务器IP:8012 office.home/opt/libreoffice7.6base.url这个容易被忽略但它很重要kkfileview 生成预览页面里的一些内部链接是以它为基准拼的。如果填的是127.0.0.1而你从外网访问页面里的部分资源就会指向本机、加载失败表现是页面出来了但样式全丢。3.3 用 systemd 把它变成开机自启的服务用bin/startup.sh手动启动关掉终端或者断开 SSH 之后服务就没了这显然不适合生产。标准做法是写一个 systemd 单元[Unit] Descriptionkkfileview file preview service Afternetwork.target [Service] Typeforking Userroot WorkingDirectory/opt/kkfileview ExecStart/opt/kkfileview/bin/startup.sh ExecStop/opt/kkfileview/bin/showdown.sh Restarton-failure RestartSec15 LimitNOFILE65535 [Install] WantedBymulti-user.target存成/etc/systemd/system/kkfileview.service然后systemctl daemon-reload systemctl enable kkfileview systemctl start kkfileview systemctl status kkfileview这里有几个点值得解释。Typeforking是必须的因为startup.sh内部是nohup ... 的方式把进程甩到后台systemd 需要知道它是派生型服务否则会误判服务已经退出。Restarton-failure加上RestartSec是为了应对偶发的启动失败——比如 LibreOffice 第一次调用要初始化用户配置目录偶尔会拖慢启动。LimitNOFILE是因为预览服务会同时打开不少文件句柄默认上限在大文件并发时容易触顶。还有一点ExecStop一定要配上。kkfileview 提供的停止脚本会顺带清理它拉起来的 LibreOffice 进程。如果没有这一步重启服务时旧的soffice进程可能还在新进程再拉一个几个来回下来机器上就是一堆僵尸进程内存悄悄被吃掉。4. Debian / Ubuntu 的差异点在哪里4.1 apt 装 LibreOffice 与字体包的组合Debian 这条线整体比 CentOS 顺因为 apt 的依赖处理更聪明LibreOffice 的打包也更完整。一条命令基本能把主力组件拉齐apt update apt install -y fontconfig openjdk-8-jdk \ libreoffice libreoffice-writer libreoffice-calc libreoffice-impress \ libreoffice-java-common \ fonts-wqy-zenhei fonts-noto-cjk注意libreoffice这个元包在 Debian 上会拉一大堆东西包括图形界面相关的组件。如果你的服务器是最小化安装、不想装一堆桌面依赖可以换成分模块装apt install -y libreoffice-core libreoffice-writer libreoffice-calc \ libreoffice-impress libreoffice-java-commonlibreoffice-core本身就包含了无界面转换需要的能力实测足够支撑 kkfileview 的转换需求。Debian 还有一个特点是包管理器的交互提示。某些版本装字体或 JDK 时会弹配置界面在自动化脚本里会卡住。提前设好非交互模式export DEBIAN_FRONTENDnoninteractive apt install -y ...这个小技巧在写部署脚本时特别救命不然 CI 流水线会莫名其妙挂在那里等人按回车。4.2 Debian 上常见的路径与权限问题Debian 系的 LibreOffice 装在/usr/lib/libreoffice而不是 CentOS 的/usr/lib64/libreoffice。大多数情况下 kkfileview 能自动探测到但如果你遇到启动正常、转换报错找不到 soffice就要在配置里显式指一下office.home/usr/lib/libreoffice另一个 Debian 上更常见的问题是运行用户和文件权限。如果你用非 root 用户跑服务这其实是更好的做法要确保这个用户对几个目录有写权限chown -R kkfile:kkfile /opt/kkfileview/log chown -R kkfile:kkfile /opt/kkfileview/file chmod -R 755 /opt/kkfileview/bin还有 LibreOffice 自己的用户配置目录。它默认会写到$HOME/.config/libreoffice如果运行用户的 HOME 不存在或者不可写转换会直接失败。稳妥的做法是在 systemd 单元里显式指定[Service] Userkkfile EnvironmentHOME/home/kkfile这个坑不常被提到但一旦撞上报错信息非常隐晦——日志里只有一句转换失败没有任何细节。我当时查了挺久才定位到是 HOME 目录不可写。4.3 一个通用的启动脚本检查清单不管是哪个发行版服务装完后我都会跑一遍这个检查清单。它帮我省下过至少三次线上救火检查项命令期望结果Java 可用java -version打印 1.8 或以上JAVA_HOME 已设echo $JAVA_HOME非空LibreOffice 可用soffice --version打印版本号中文字体已装fc-list :langzh | wc -l大于 0端口未被占ss -lntp | grep 8012空目录可写touch /opt/kkfileview/log/.t rm -f /opt/kkfileview/log/.t无报错日志能出tail -f /opt/kkfileview/log/kkfileview.log有启动完成提示最后一条尤其重要。服务能不能用不看curl返回码看日志里有没有启动完成和后续的转换记录。kkfileview 的日志写得还算清楚转换耗时、缓存命中、异常堆栈都能看到排查时优先看它。5. Docker 部署省事的路线以及它藏起来的两件事5.1 单容器跑起来的完整命令如果服务器上已经有 Docker 环境用镜像是更省事的路径因为它把 JDK 和 LibreOffice 都打进去了不用你自己纠结版本。docker pull keking/kkfileview:latest docker run -d \ --name kkfileview \ --restartunless-stopped \ -p 8012:8012 \ -e JAVA_OPTS-Xms1g -Xmx2g \ -v /opt/kkfileview/config:/opt/kkfileview/config \ -v /opt/kkfileview/log:/opt/kkfileview/log \ -v /opt/kkfileview/file:/opt/kkfileview/file \ keking/kkfileview:latest几个参数的实际意义--restartunless-stopped保证宿主机重启后容器能自动拉起等价于前面 systemd 的enableJAVA_OPTS是给容器内 JVM 传参三个-v分别把配置、日志、缓存目录挂出来这样容器重建时你的配置和缓存不会丢日志也能在宿主机上直接看。这三个挂载强烈建议一个都别省。启动完成后访问http://服务器IP:8012应该能看到首页。5.2 挂载字体与配置目录的正确姿势Docker 路线第一个藏着的问题是字体。镜像里通常只带了几套基础字体中文覆盖不一定完整。如果你发现预览出来的中文是方块别急着怀疑配置先往字体上想。解决办法是把宿主机的字体目录挂进去-v /usr/share/fonts:/usr/share/fonts:ro以只读方式挂载容器里直接复用宿主机的字体资源。这样你在宿主机上按 2.3 的方式装好字体、刷好缓存容器重新拉起后就能直接用了。注意宿主机上也要装fontconfig并执行过fc-cache -fv否则挂进去的字体的缓存信息是缺的容器里同样认不出来。第二个藏着的问题是文件源的网络可达性。容器有自己的网络命名空间容器能不能访问到那个文件地址和宿主机能不能是两回事。如果你传的是内网域名或者宿主机本地路径容器里很可能解析不到。两种处理方式一是用--network host让容器直接复用宿主机网络栈二是把文件源配置成容器可达的地址比如用宿主机的内网 IP 而不是127.0.0.1。127.0.0.1在容器里指的是容器自己这个是最容易犯的错。5.3 什么时候反而不推荐 DockerDocker 省事但不是万能。有两种情况我更倾向裸机部署。一种是需要精细控制 LibreOffice 版本的场景。企业里对文档转换效果的要求有时很具体比如某个版本的 LibreOffice 对某种表格样式的还原更好而镜像里的版本你没得选。这时候裸机部署、自己装指定版本的 tar.gz 反而更自由。另一种是要求极致性能的场景。容器化会带来一点点额外的开销而且容器内进程数量、文件描述符上限的控制链路更长。高并发转换场景下裸机 systemd 的调优空间更大也更容易做进程级别的资源隔离。当然这两种情况的差异在日常量级下基本感知不到除非你的预览量真的很大否则别为了性能这个理由放弃 Docker 的便利。6. application.properties 里真正需要你动的那几行6.1 端口、上下文路径与 base.urlserver.port默认是8012一般不用改除非端口冲突。真要改的话记两件事改完配置文件别忘了同步改防火墙规则和任何前置转发层的配置。base.url前面提过这里再强调一次它的判断标准你在浏览器地址栏里输入的那个前缀就是它该填的值。比如你最终是通过http://preview.内网域名/kkfileview/访问的那base.url就应该填这个而不是http://127.0.0.1:8012。如果你的服务前面有一层 Web 服务器做统一入口还要注意上下文路径的一致性。假设你把请求路径收敛到/kkfileview/下那么配置里的上下文路径和转发层的前缀要能对上同时base.url也要带上这个前缀。这三处只要有一处不一致典型现象就是首页能开点预览就 404因为首页是静态资源能兜住但预览接口的拼装路径错了。6.2 缓存类型的选择default、jdk 还是 redis缓存这块是决定性能上限的关键配置。常见的取值有这么几种用途差别挺大缓存类型存储位置适用场景主要风险default内存 本地磁盘单机、量不大磁盘会被缓存文件吃掉jdk纯内存基于 JVM单机、追求速度大文件容易把堆撑爆redis外部 Redis多实例、需要共享依赖外部服务可用性单机小规模用default最省事它是内存加磁盘的混合策略大文件落在磁盘上不占堆。但你要记得配一个缓存清理任务否则磁盘是温水煮青蛙式的被吃满cache.clean.enabledtrue cache.clean.cron0 0 3 * * ?多实例部署就必须上redis否则每台机器各缓存一份用户刷新几次可能落到不同实例上缓存命中率惨不忍睹cache.typeredis spring.redisson.addressredis://127.0.0.1:6379jdk这个选项我要多说一句它快但也危险。纯内存缓存意味着一个大文件转出来的中间产物全在堆里几十兆的文件叠加上并发OutOfMemoryError就是几分钟的事。除非你的文件都确定很小否则别选它。6.3 转换超时、水印与下载开关超时配置是另一个必须调的项。默认值往往偏保守遇到大文档、复杂排版的表格转换时间很容易超出表现就是页面一直转圈最后报超时。可以适度放宽office.task.timeout60单位一般是秒具体看你手上的版本注释。我的经验是先把超时调到 60 秒跑一段时间观察日志里的实际转换耗时分布再决定要不要继续往上调。盲目设成 300 秒的后果是真卡住的请求会占着转换槽位三分钟把后面排队的全堵死。水印是个很实用的安全特性预览敏感文档时建议打开watermark.txt内部资料 请勿外传 watermark.fontsize18 watermark.alpha0.3 watermark.x.space200 watermark.y.space200alpha控制透明度x.space和y.space控制水印之间的间隔。间隔太小会糊成一片影响阅读太大又容易被裁掉我一般用 200 左右。还有一个容易被忽略的开关是是否允许下载原始文件。预览服务一旦同时提供下载入口等于把文件通过预览服务二次暴露了一遍。如果你的业务本身对文件访问有权限校验那这个开关最好关掉把下载能力收回业务系统里。7. 预览接口怎么调URL 参数的编码规则7.1 onlinePreview 的参数拼装接口本身很简单核心就一个/onlinePreview文件地址放在url参数里。但这个url参数不能直接放原始地址中间要做两重编码这是新手最容易翻车的地方。正确的做法是先把原始文件地址做 Base64再对整个 Base64 结果做 URL 编码。用 Java 写出来是这样String fileUrl http://fileserver/contract/2024年合同.docx; // 第一步Base64 编码用 UTF-8中文路径才不出乱码 String base64 Base64.getEncoder() .encodeToString(fileUrl.getBytes(StandardCharsets.UTF_8)); // 第二步URL 编码 String encoded URLEncoder.encode(base64, UTF-8); String previewUrl http://127.0.0.1:8012/onlinePreview?url encoded;为什么第二步不能省因为 Base64 的结果里会出现、/、这三种字符。其中在 URL 的查询字符串里有特殊含义——它会被解析成空格。如果不做 URL 编码你的文件地址在服务端解出来就少了一个字符或者多了一个空格结果就是文件不存在或者 404。URLEncoder会把转成%2B这个问题就规避了。这段代码看起来简单但因为导致的 404 我见过不止一次排查起来还挺费劲因为参数看起来就是对的。除了url还支持一些附加参数常用的有参数作用示例url文件地址Base64 URL 编码必填fullfilename指定完整文件名影响扩展名判断fullfilename报表.xlsxwatermarkTxt单次预览的水印文字watermarkTxt张三 2024-06-01fullfilename这个参数的实际价值在于当你的文件地址末尾没有扩展名时比如走的是/api/file/12345这种接口地址kkfileview 无法从 URL 判断文件类型这时候就必须靠fullfilename告诉它这是xlsx还是docx。不带这个参数它就只能按默认逻辑猜猜错就会走到错误的转换分支。7.2 文件来源的两种模式远程 URL 与本地目录文件来源有两种典型模式选哪种直接决定了你的部署拓扑。模式一是远程 URL。业务系统提供文件访问地址kkfileview 主动去拉。这个模式的优点是解耦文件和预览服务可以不在同一台机器上。缺点是网络可达性成了前置条件而且要注意服务端发起请求时的身份问题——如果你的文件接口需要鉴权kkfileview 拉不到文件除非你提供一个临时可访问的地址比如带签名的临时链接。这里还有个安全点新版本一般会有可信主机白名单的配置如果预览远程地址时报不受信任之类的错误先去检查这个白名单有没有把你的文件服务器加进去这是防止服务被当成任意请求跳板的保护机制别直接关掉。模式二是本地目录。配置一个本地目录把文件放进去或者挂载进去url传相对路径就能预览。优点是快、安全、不依赖外部网络。缺点也很明显文件得先落到这台机器上而且路径处理必须严谨任何允许相对路径的能力都要防住路径穿越否则一个../../就能读到系统文件。生产环境用这个模式的话建议单独切一块磁盘挂载到配置的目录下把权限收窄只给运行用户读写。我的建议是内部小规模、文件本来就在同一台机器上的用本地目录文件散落在多个业务系统里的用远程 URL但把白名单和临时鉴权地址这两件事做扎实。8. Nginx 前置转发与访问入口的收口8.1 转发配置与常见的 404/502如果你前面有一层 Nginx 之类的 Web 服务器做统一入口有几个点必须对齐否则症状都很像服务没起来实际上服务好好的。第一是路径前缀的一致性。入口前缀、转发目标路径、配置里的base.url这三者必须是一个闭环。最常见的事故是入口配了/kkfileview/但没有把前缀剥离结果转发到后端的路径变成了/kkfileview/onlinePreview而 kkfileview 只认/onlinePreview于是 404。第二是请求头里的 Host 和协议。如果你的入口做了 HTTPS 终止后端收到的是 HTTP这时后端拼出来的部分资源链接可能还是 HTTP在浏览器里就会被拦。处理方式是让转发层把原始协议和 Host 透传过去应用侧也配置上对应的识别参数。第三是502 的两种典型来源。一种是后端确实没起来先去curl http://127.0.0.1:8012确认另一种是转换耗时超过了转发层的超时时间。后者的特点是首页正常、小文件正常只有大文件报 502。这时候要调的是转发层的读超时不是服务本身。8.2 大文件、长耗时的超时设置这块我有过很具体的一次经历一个 40 多页的带图表格xlsx本地直连 8012 端口预览没问题走统一入口就必报错。原因就是转换耗时接近 30 秒而入口层默认超时也是 30 秒正好卡在边界上。要调的参数有这么几类配置方向调什么建议思路转发层读超时读取响应的最大等待时间设为后端转换超时的 1.5 倍以上请求体大小单次请求最大体积如果走上传模式按最大文件设缓冲开关是否缓冲后端响应大量小文件场景下适度关闭缓冲连接复用到后端的连接池高并发时开启并设置合理上限一个原则转发层的超时必须大于应用层的转换超时而且要留出余量。两者相等是最糟糕的配置因为总有一部分请求正好卡在边界上表现就是时好时坏这种问题最难排查。另外提醒一句如果走 HTTPS 并且是自建证书别忘了把证书链配对否则某些客户端会拒绝连接症状同样是服务不可用但根因完全不在 kkfileview 这边。9. 上线后高频故障的排查链路按现象倒推9.1 页面转圈不出内容这是收到最多的报障。不要一上来就重启服务按下面这条链路走基本三轮内能定位。第一步curl http://127.0.0.1:8012看首页是否正常。首页都不出问题在服务层去看log/kkfileview.log的启动日志重点看端口是否被占、依赖是否缺失。第二步首页正常但预览转圈在服务器上直接curl一下那个文件地址验证机器能不能拿到文件。这一步能排掉一大半问题DNS 解析不到、防火墙没放行、需要鉴权、容器网络隔离全在这一步暴露。第三步文件能拿到但还是转圈去看日志里的转换记录。如果日志显示转换已经开始但没有结束那就是 LibreOffice 卡住了去看 9.2。如果日志里根本没有转换记录那是请求没走到转换环节回头检查 URL 参数的编码对不对——尤其是那个号的问题。第四步日志显示转换完成但页面还是空的那就是前端渲染或缓存的问题。先清一下缓存目录再换个浏览器或者无痕窗口试排除浏览器本地缓存的干扰。9.2 Office 文件转换失败 / LibreOffice 进程残留这是 kkfileview 运维里最核心的一类问题值得单独拿出来说。LibreOffice 有个特点当一个转换进程因为文件异常、内存不足或者超时而崩溃时它不一定能自己清理干净。残留的soffice进程会继续占着资源而且它可能锁住了用户的配置目录导致后续新的转换进程启动时直接失败。表现就是一开始个别文件转换失败过一段时间全部文件都失败。排查命令很简单ps -ef | grep soffice | grep -v grep如果看到一堆残留进程先别急着kill -9。优先用kill让它自己退出因为强杀可能留下锁文件。杀完之后去清理一下 LibreOffice 的用户配置锁# 路径按运行用户的 HOME 来 ls -la /home/kkfile/.config/libreoffice/4/.lock # 确认没有活着的进程后可以删除 rm -f /home/kkfile/.config/libreoffice/4/.lock根治的办法有两个方向。一是给转换任务配上超时和自动清理kkfileview 本身有超时机制但你要确保它真的生效超时参数别设得太离谱。二是在部署层面加一个兜底巡检写个定时任务发现soffice进程存活时间超过某个阈值就清掉。这个兜底看着土但在实际运行中非常管用因为总会有那么几个畸形文件能让 LibreOffice 挂住。9.3 内存与磁盘被忽略的两个瓶颈转换是吃内存的。LibreOffice 加载一个大pptx或者带大量图片的docx瞬时内存占用可能到几百兆甚至上 G。如果你同一台机器跑好几个并发转换内存曲线会非常陡。内存不够的表现不是报错而是慢和偶发失败。JVM 堆不够会OOMLibreOffice 内存不够可能被系统 OOM Killer 干掉日志里只有一句进程消失。所以别只看 Java 进程的堆还要留足物理内存给 LibreOffice。经验值是每个并发转换槽位预留 500M 到 1G 的额外内存再算上 JVM 的-Xmx。磁盘的问题更隐蔽。缓存目录会随着预览量持续增长而缓存清理如果不配它就是只进不出。定期看一下缓存目录的体积du -sh /opt/kkfileview/file/*如果发现涨得很快一是检查缓存清理任务有没有真的在跑二是看看缓存的有效期配置是不是太长了。另外转换过程中的临时文件也在这块盘上磁盘满了的直接后果是所有转换都失败而且报错信息往往指向别处很容易误判。10. 多实例与容量规划的几条经验10.1 共享缓存与文件源单机扛不住的时候就要上多实例而多实例最关键的两个词是共享。共享缓存就是前面说的把cache.type换成redis。这不只是为了命中率还有一个更重要的原因避免同一份文件在多台机器上重复转换。一份大文档在两台机器上各转一遍两倍的资源消耗而用户只点了一次。有了共享缓存第一台转完写进 Redis第二台的请求直接命中成本立刻降一半。共享文件源也要考虑。如果用的是本地目录模式多实例下你就得保证每台机器的那个目录内容一致这个维护成本很高。更现实的方案是统一走远程 URL让文件服务成为唯一的真相来源。这样新增实例就是纯粹的横向扩容不需要同步数据。Redis 这块有两个坑一是它自己也可能成为单点生产上至少做主从二是缓存里的内容会包含文件转换结果如果文件本身敏感Redis 的访问控制和持久化策略要及时跟上别让一个临时的预览缓存变成数据泄露的口子。10.2 JVM 参数与并发数的估算最后聊聊参数怎么估。我的做法是先测再定不靠拍脑袋。先做单文件基准测试找几个典型文件最小的、最大的、最复杂的在单实例上跑看日志里的转换耗时。假设最复杂那个文件平均要 8 秒那理论上一个转换槽位每秒能处理 0.125 个请求。如果业务峰值是每分钟 60 次预览那就是每秒 1 个请求需要大概 8 个并发槽位才能不排队。这个数字再乘个 0.7 的安全系数实际按 10 到 12 个槽位准备。再根据槽位算内存JVM 堆 槽位数 × 单次转换峰值内存 × 安全系数。这是物理内存的下限。如果算出来的数字超过单机能力就该考虑拆机器而不是硬堆配置因为转换这件事本质上是个 CPU 和内存密集型任务堆配置的边际收益衰减很快。JVM 参数上-Xms和-Xmx设成相等是基础操作-XX:UseG1GC这类现代 GC 在预览这种短时高频分配的场景下表现更好。至于线程池相关参数不要一上来就调大先把默认值跑起来观察日志里排队和拒绝的情况再动否则很容易调成看起来并发很高实际上全在互相争抢。最后聊点实在的。我自己在两套环境上跑 kkfileview一套裸机 CentOS、一套 Docker 在 Debian 上最大的体会是这套服务本身的部署难度其实不高真正花时间的地方全在外围——字体、LibreOffice 进程、缓存目录、以及前面那层转发的时间参数。所以我的习惯是把 4.3 那张检查清单做成一键脚本每次上新的环境先跑一遍能省掉大量服务明明起来了就是不能用的无效排查。另外强烈建议第一次部署时拿一个带图表的复杂xlsx和一个几十页的docx做验收别拿一个纯文本文件测试通过就以为搞定了那两类文件才是暴露问题的主力。