上周有个读者在群里问,他们公司内部知识库想把上传的 Word、Excel、PPT 直接在线打开,不想让用户下载后再看,问我有没有轻量方案。我第一反应就是 kkfileview,一个用 Java 写的开源文档在线预览服务。这类需求其实很普遍,像 OA、合同管理、网盘、工单系统,只要涉及附件上传,几乎都会遇到“文件传上来了,怎么让用户直接在浏览器里看”的问题。kkfileview 就是解决这个问题的。它支持 doc、docx、xls、xlsx、ppt、pptx、pdf、txt、图片、音频、视频、压缩包等格式,部署也不复杂,Docker 一条命令就能跑起来。我前后在生产环境里用过三套部署:Docker 单机、源码定制、以及配合 Redis 的多实例,踩过的坑不少。下面我按“选型—准备—安装—使用—排查—生产”这个顺序,把整个流程拆开讲一遍,适合刚接触这个项目的新手,也适合准备上生产的运维和开发。
1. 为什么选 kkfileview:核心定位与方案对比
1.1 文件预览的常见痛点与 kkfileview 的切入方式
很多人第一次做附件预览,最容易想到的是前端直接渲染。图片和 PDF 确实可以,但遇到 Office 文件就头疼了:浏览器原生打不开 docx,xlsx 更是没法直接显示;用前端 js 库,比如 mammoth.js 能转 docx,但复杂表格、页眉页脚、公式基本会丢;SheetJS 能解析 xlsx,可样式和打印效果很难保证。再往后考虑服务端转换,用 LibreOffice 或 OpenOffice 把 Office 转成 PDF,再让浏览器看 PDF,这条路线稳定得多,但需要自己封装转换队列、处理并发、管理临时文件,工作量并不小。
kkfileview 的核心价值就在于,它把“服务端转换 + 前端预览”这一整套流程打包好了。你不需要自己调 LibreOffice 命令行,也不用手写 PDF 分页预览。它对外只暴露一个 HTTP 接口,传入文件的 URL,返回一个可嵌入 iframe 的预览页面。对于业务系统来说,集成成本极低:前端只要拼一个链接,iframe 一嵌,用户就能看。这种“一个接口解决所有格式”的思路,特别适合后台管理系统、知识库、工单附件、合同查阅这类场景。
我自己最早是用 OpenOffice 转换,脚本写了三百多行,最怕遇到并发转换把进程卡死,后来换成 kkfileview,最直观的感受是省心。它内部对转换进程有超时控制,支持缓存,还能配置水印和禁止下载。当然,它也不是万能的,比如超大 Excel 的渲染、复杂排版的 Word,效果取决于 LibreOffice 的转换质量,但日常办公文件 95% 以上都能正常预览。
1.2 与其他预览方案横向对比
市面上做文件预览的方案很多,我把常见的几类列出来,方便你判断 kkfileview 到底适合什么位置。对比维度包括部署难度、支持格式、样式还原、二次开发成本、资源占用。
| 方案类型 | 代表工具/服务 | 支持格式 | 样式还原度 | 部署难度 | 资源占用 | 适合场景 |
|---|---|---|---|---|---|---|
| 前端纯 JS 解析 | mammoth.js、SheetJS | docx、xlsx 为主 | 一般,样式易丢 | 很低 | 低 | 简单文本、表格预览 |
| 服务端转换 | LibreOffice + 自研接口 | Office、PDF、图片 | 较高 | 中等偏高 | 中高 | 有开发能力、需深度定制 |
| 开源预览服务 | kkfileview | Office、PDF、图片、音视频、压缩包 | 较高 | 低到中等 | 中高 | 后台系统、知识库、OA |
| 商业云预览 | 各家云文档服务 | 很全 | 很高 | 低 | 按量付费 | 不差钱、不想运维 |
| 浏览器原生 | PDF.js、图片标签 | PDF、图片 | 高 | 低 | 低 | 仅 PDF/图片 |
从表格能看出来,kkfileview 的定位非常清晰:它比纯前端方案支持格式多得多,比自研转换方案省人力,又比商业云服务可控、免费。如果你需要一个能自己部署、支持格式全、集成简单的预览服务,kkfileview 基本是首选。有人会拿 open file view 和 kkfileview 做对比,前者更偏向轻量查看,后者在格式覆盖、缓存、水印、集群这些生产特性上更完整。实际选型时,先看你的文件类型,如果主要是 Office 和 PDF,kkfileview 足够。
还有一点值得说:kkfileview 是 Java 技术栈,和大多数国内后台系统(Spring Boot、Spring Cloud)天然亲近。你可以把它当成一个独立服务部署,也可以把源码拉下来嵌入自己的项目。对于运维来说,Docker 镜像已经内置了 LibreOffice 和常用字体,少去了很多环境折腾。对于开发来说,接口简单,文档清楚,遇到问题查 issue 也方便。
1.3 部署形态选择:Docker 还是源码
决定用 kkfileview 之后,第一个要做的选择就是部署形态。Docker 部署最省事,官方镜像里已经装好了 LibreOffice、字体、JDK,拉下来就能跑。适合快速验证、中小规模生产、不想折腾系统的团队。源码部署更适合需要改源码、定制水印逻辑、调整转换参数、集成自己认证体系的场景。比如我们有个项目要求预览链接必须带一次性 token,且要记录谁在什么时候看了哪个文件,这种就要改源码或者在外层加网关。
Docker 的缺点也有:镜像体积不小,通常 1GB 以上;如果要装特殊字体,得自己打镜像或者挂载字体目录;容器内 LibreOffice 转换有时会遇到权限问题。源码部署的缺点是环境依赖多,JDK 版本、LibreOffice 版本、字体、中文字体缓存,每一步都可能翻车。我的建议是:先用 Docker 跑通,确认功能满足;如果后续需要深度定制,再拉源码。不要一上来就源码部署,容易在环境上耗掉一整天。
另外,如果你的文件量很大,比如每天几万次预览,单机 Docker 可能扛不住,这时候要考虑多实例 + Redis 缓存 + Nginx 负载均衡。这个后面会详细讲。先明确一点:kkfileview 本身是无状态服务,转换结果可以缓存到 Redis 或本地,多实例部署并不复杂。但前提是你把临时目录和缓存配置好,否则会出现 A 实例转换、B 实例读取不到的问题。
2. 环境准备:安装前的依赖清单与参数规划
2.1 操作系统与硬件建议
kkfileview 官方推荐 Linux,实际在 CentOS 7/8、Ubuntu 20.04/22.04、Debian 上都能跑。Windows 也能部署,但生产环境不建议,因为 LibreOffice 在 Windows 下的进程管理和字体路径更容易出问题。我自己的生产环境用的是 Ubuntu 22.04 LTS,内核 5.15,跑了两年比较稳。如果你用 CentOS,注意 7 已经停止维护,尽量换 Rocky Linux 或 AlmaLinux。
硬件方面,官方没有特别高的要求,但文件预览是 CPU 和内存密集型操作,尤其是 Office 转 PDF。我的经验值:2 核 4G 可以支撑每天几百次预览,4 核 8G 可以支撑每天几千次,8 核 16G 以上适合上万次并配合缓存。内存主要吃在 LibreOffice 进程和 JVM 堆上,如果同时转换多个大文件,内存会飙升。磁盘方面,临时文件目录要有足够空间,一个 50MB 的 Word 转 PDF 可能产生几百 MB 临时文件,建议至少预留 20GB,并定期清理。
还有一点容易忽略:文件系统。如果挂载的是 NFS 或网络存储,LibreOffice 读写临时文件可能很慢,甚至锁不住。尽量用本地 SSD。如果必须用网络存储,把临时目录指向本地盘,转换完成后再把结果写回网络存储。这个坑我在一个客户现场遇到过,预览一个 10MB 的 PPT 要 20 秒,换成本地盘后降到 3 秒。
2.2 必需依赖:JDK、LibreOffice、字体、Redis
Docker 部署时这些依赖都打包好了,但源码部署必须自己装。JDK 要求 1.8 以上,建议 JDK 11 或 17。LibreOffice 是核心,版本建议 7.0 以上,太低会有些新格式不支持。安装命令在 Ubuntu 下是apt install libreoffice libreoffice-l10n-zh-cn,CentOS 下可以用 yum 或直接下载 tar 包。注意不要装 OpenOffice,kkfileview 默认走 LibreOffice。
字体是中文预览的命门。Linux 服务器默认只有少量西文字体,中文 Word 转 PDF 后全是方块。必须安装中文字体,比如文泉驿、思源黑体、宋体、微软雅黑。我通常把 Windows 的C:\Windows\Fonts里常用字体拷贝到/usr/share/fonts/chinese,然后执行fc-cache -fv刷新缓存。注意版权问题,生产环境用开源字体更稳妥,比如fonts-wqy-zenhei、fonts-noto-cjk。装完用fc-list :lang=zh检查是否识别到中文。
Redis 是可选的,但强烈建议生产环境启用。kkfileview 默认用本地缓存,多实例会不一致。配置 Redis 后,转换结果和文件信息可以共享,还能设置过期时间,避免磁盘被撑满。Redis 安装本身不复杂,但要注意和 kkfileview 的网络延迟,尽量同机房。如果不用 Redis,至少要把本地缓存目录挂载到持久化盘,并设置定时清理。
2.3 端口与目录规划、内存计算
kkfileview 默认端口是 8012,这个端口不冲突的话直接用。如果前面有 Nginx,可以把 8012 只监听内网,外网走 80/443 反向代理。目录规划建议分成三块:配置目录、临时文件目录、日志目录。Docker 部署时通过-v挂载,源码部署时在application.properties里指定。我通常这样规划:
- 配置目录:
/data/kkfileview/config - 临时文件目录:
/data/kkfileview/file - 日志目录:
/data/kkfileview/logs
内存计算有个粗略公式:JVM 堆 + LibreOffice 单进程内存 × 并发数 + 系统缓存。LibreOffice 转换一个普通 Office 文件大约占用 200-500MB 内存,大文件可能上 1GB。假设你允许同时转换 5 个文件,JVM 堆设 2GB,那么总内存至少 2GB + 5×500MB + 系统 2GB ≈ 6.5GB,所以 8GB 内存比较稳妥。JVM 参数可以在启动脚本里设置-Xms1g -Xmx2g,不要设太大,否则系统缓存不够反而变慢。
另外,文件上传大小限制也要提前规划。kkfileview 默认可能限制 100MB,可以在配置文件里改spring.servlet.multipart.max-file-size和max-request-size。如果你的业务有 500MB 的视频或压缩包,记得同步调整 Nginx 的client_max_body_size。这些参数不提前规划,上线后用户传个大文件就会报错。
2.4 安装包获取与校验
Docker 方式直接拉镜像,命令是docker pull keking/kkfileview:4.4.0。建议固定版本号,不要用 latest,避免自动升级带来不兼容。拉取后可以用docker images看镜像大小,通常 1.5GB 左右。如果公司网络不能直接拉 Docker Hub,可以找国内镜像源,或者把镜像导出成 tar 包再用docker load导入。源码方式从 GitHub 或 Gitee 拉取,注意选择稳定分支,比如v4.4.0。
下载后一定要校验。Docker 镜像可以用docker inspect看构建时间,源码可以核对 commit id 或 release 包哈希。我见过有人从第三方下载了被篡改的 jar,结果启动后疯狂外连,虽然是极端情况,但生产环境必须走官方渠道。如果是离线环境,提前把镜像、LibreOffice 安装包、字体包都准备好,避免现场抓瞎。
3. Docker 方式安装 kkfileview:最快跑通的路径
3.1 拉取镜像与启动命令逐行拆解
Docker 部署是我最推荐的上手方式。下面这条命令可以直接复制,但每一段都要理解:
docker run -d \ --name kkfileview \ --restart=always \ -p 8012:8012 \ -e TZ=Asia/Shanghai \ -v /data/kkfileview/config:/opt/kkfileview/config \ -v /data/kkfileview/file:/opt/kkfileview/file \ -v /data/kkfileview/logs:/opt/kkfileview/logs \ keking/kkfileview:4.4.0逐行解释:-d后台运行;--restart=always让容器随 Docker 启动,服务器重启后自动恢复;-p 8012:8012映射端口;-e TZ=Asia/Shanghai设置时区,这个非常重要,否则日志时间和文件过期时间会差 8 小时;-v挂载配置、临时文件、日志目录,保证容器删了数据还在。镜像名keking/kkfileview:4.4.0是官方仓库,版本号按需改。
启动后执行docker logs -f kkfileview,看到 “Started KKFileViewApplication” 就算成功。如果失败,多半是端口占用或挂载目录权限不对。挂载目录要先mkdir -p并给写权限,比如chmod 777 /data/kkfileview,生产环境可以给特定用户,但简单起见先放开。注意,如果你不挂载 config,容器内用的是默认配置,改了也不持久,所以一定要挂。
还有一个细节:官方镜像内 LibreOffice 的安装路径可能是/opt/libreoffice7.6,配置文件里office.home要指向它。如果你自己打镜像换了路径,记得同步改application.properties。Docker 部署省事就省在这里,默认配置基本能跑,不用自己装 LibreOffice。
3.2 挂载配置文件与自定义预览参数
第一次启动后,容器内会生成默认的application.properties。你可以执行docker cp kkfileview:/opt/kkfileview/config/application.properties /data/kkfileview/config/把它拷出来,然后修改。重点参数我列几个:
server.port=8012:服务端口。file.upload.max-size=100MB:上传大小限制。cache.enabled=true:开启缓存。cache.type=redis:缓存类型,默认是default本地缓存,生产建议 redis。spring.redis.host=127.0.0.1:Redis 地址。watermark.enabled=true:开启水印。watermark.txt=内部资料:水印文字。office.preview.type=pdf:Office 预览方式,可选 pdf、image。office.home=/opt/libreoffice7.6:LibreOffice 路径。
改完配置后重启容器:docker restart kkfileview。如果配置有语法错误,容器会启动失败,看日志就能定位。我建议把配置文件纳入版本管理,每次改动记录原因。比如水印文字根据租户不同,可以做成环境变量注入,但 kkfileview 原生不支持多租户水印,需要改源码或外层代理。
还有一个常用参数是file.dir,指定临时文件目录。默认可能在/opt/kkfileview/file,挂载后就是宿主机的/data/kkfileview/file。这个目录会随着预览次数增长,必须定期清理。可以写个 crontab,每天凌晨删除 7 天前的文件:find /data/kkfileview/file -type f -mtime +7 -delete。注意不要删正在使用的文件,最好在业务低峰期执行。
3.3 验证部署:健康检查与预览测试
容器起来后,先访问http://服务器IP:8012,如果能看到 kkfileview 的首页,说明服务正常。接着用一个真实的文件测试。最简单的办法是准备一个公网可访问的 PDF 或 Word 链接,然后拼预览地址:
http://服务器IP:8012/onlinePreview?url=Base64编码后的文件URL注意,Base64 编码后还要做 URL 编码,否则+、/、=会出问题。你可以用在线工具先测,比如文件 URL 是http://example.com/test.docx,Base64 后是aHR0cDovL2V4YW1wbGUuY29tL3Rlc3QuZG9jeA==,再 URL 编码得到aHR0cDovL2V4YW1wbGUuY29tL3Rlc3QuZG9jeA%3D%3D。拼起来访问,如果能正常预览,说明部署成功。
如果预览报错,先看容器日志。常见错误有:连接文件 URL 超时、LibreOffice 转换失败、字体缺失。如果是文件 URL 是内网地址,kkfileview 容器需要能访问到,否则会 404。测试时尽量用公网文件,或者把文件放到容器能访问的内网 HTTP 服务上。另外,浏览器控制台如果有跨域错误,是因为你用了 iframe 嵌入,kkfileview 默认允许跨域,一般不用改。
3.4 Docker 部署的常见坑与修复
第一个坑:时区不对。表现是预览链接过期时间、日志时间差 8 小时。解决就是加-e TZ=Asia/Shanghai,并且挂载/etc/localtime:/etc/localtime:ro也可以。第二个坑:字体缺失。中文 Word 预览全是方块。解决是挂载字体目录,比如-v /usr/share/fonts:/usr/share/fonts:ro,但更推荐自己构建镜像时把字体 COPY 进去。第三个坑:权限问题。挂载目录如果属主是 root,容器内用户可能写不进去,日志报 Permission denied。解决是chmod -R 777或指定用户。
第四个坑:LibreOffice 进程残留。长时间运行后,容器内可能有一堆soffice.bin僵尸进程,导致内存耗尽。解决是设置转换超时,kkfileview 有office.convert.timeout参数,默认可能 300 秒,可以调小到 120 秒。同时定期重启容器,比如每天凌晨重启一次。第五个坑:镜像体积大,拉取慢。可以配置 Docker 镜像加速,或者用离线 tar 包。这些坑我都踩过,最影响生产的是字体和进程残留,前者导致用户看到乱码,后者导致服务假死。
4. 源码方式安装 kkfileview:可控性更强的部署
4.1 拉取源码与目录结构速览
源码部署适合需要定制的情况。先拉代码:
git clone https://github.com/kekingcn/kk-file-view.git cd kk-file-view git checkout v4.4.0目录结构大致是:src/main/java放 Java 代码,src/main/resources放配置和模板,web放前端静态资源。核心包有controller、service、utils、config。OnlinePreviewController是对外接口,OfficeConvertService负责调 LibreOffice,CacheService管缓存。如果你想改水印,找WatermarkUtils;想改文件类型白名单,找FileTypeUtils。先花半小时看一遍目录,后面改起来不迷路。
源码依赖 Maven,确保本地有 Maven 3.6+ 和 JDK 11。编译前先检查pom.xml里的版本,有些依赖可能从中央仓库拉不到,需要配国内镜像。编译命令:
mvn clean package -DskipTests成功后会在target目录生成kk-file-view.jar。如果编译报错,多半是 JDK 版本不对或依赖下载失败。可以加上-U强制更新依赖。打包后先本地运行测试:
java -jar target/kk-file-view.jar --spring.config.location=file:/data/kkfileview/config/application.properties看到启动日志后,用同样的 onlinePreview 接口测试。
4.2 修改配置文件 application.properties 关键项
源码部署的配置文件和 Docker 版基本一致,但路径要根据实际环境改。比如office.home要指向你安装的 LibreOffice 目录,常用的是/opt/libreoffice7.6或/usr/lib/libreoffice。file.dir要指向有写权限的目录,比如/data/kkfileview/file。如果要用 Redis,配置spring.redis.host、port、password。如果要用本地缓存,设置cache.type=default,并确保cache.dir可写。
还有一个重要配置是server.tomcat.max-threads,默认可能 200,如果并发预览多,可以调大到 500。但要注意内存,线程越多,同时转换的文件越多。可以配合office.convert.max-threads限制同时转换数,比如设为 4,避免 LibreOffice 把 CPU 跑满。这个参数很关键,我见过有人不限制,结果 10 个用户同时预览大 Excel,服务器直接卡死。
日志配置也建议改。默认日志可能只输出到控制台,生产环境要输出到文件并切割。可以在application.properties里设置logging.file.name=/data/kkfileview/logs/kkfileview.log,并用 logback 配置按天切割。这样出问题可以回溯。另外,把logging.level.cn.keking=debug打开,能看到更多转换细节,但会增大日志量,排障后再关掉。
4.3 编译打包与启动脚本
编译完成后,不要直接java -jar裸跑,最好写个启动脚本,设置 JVM 参数和时区。示例:
#!/bin/bash export JAVA_HOME=/usr/lib/jvm/java-11-openjdk export PATH=$JAVA_HOME/bin:$PATH nohup java -Xms1g -Xmx2g \ -Dfile.encoding=UTF-8 \ -Duser.timezone=Asia/Shanghai \ -jar /data/kkfileview/kk-file-view.jar \ --spring.config.location=file:/data/kkfileview/config/application.properties \ > /data/kkfileview/logs/start.log 2>&1 &-Dfile.encoding=UTF-8防止中文乱码,-Duser.timezone设置时区。-Xms和-Xmx设成一样可以避免堆动态调整带来的抖动。启动后tail -f看日志,如果报Address already in use,说明端口冲突,改server.port或杀进程。如果报No such file,检查配置路径。
做成 systemd 服务更规范,可以开机自启、崩溃重启。写一个/etc/systemd/system/kkfileview.service,ExecStart指向启动脚本,Restart=always。这样比 nohup 靠谱。如果你公司有容器平台,还是建议用 Docker,源码方式维护成本更高,升级要重新编译打包。
4.4 与 Nginx 配合:反向代理与 URL 编码处理
生产环境一般不会直接暴露 8012,而是通过 Nginx 反向代理,加 HTTPS、限流、认证。配置示例:
server { listen 80; server_name preview.example.com; location / { proxy_pass http://127.0.0.1:8012; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; client_max_body_size 200m; proxy_read_timeout 300s; } }注意proxy_read_timeout要调大,因为 Office 转换可能超过 60 秒。如果预览大文件频繁超时,Nginx 会报 504。client_max_body_size根据最大文件调整。如果开了 HTTPS,iframe 嵌入的页面也要用 HTTPS,否则浏览器会拦截混合内容。
URL 编码是 Nginx 代理里最容易出问题的地方。onlinePreview?url=xxx里的xxx是 Base64 再 URL 编码,Nginx 默认会解码一次,如果后端再解一次可能出错。实测下来,只要代理层不做额外的rewrite解码,直接透传即可。如果遇到+变成空格,检查前端编码是否正确。我通常在前端生成链接时就用encodeURIComponent包一层,后端接收后用URLDecoder解一次,再 Base64 解。这个顺序不能乱。
5. 核心使用:如何在自己的系统里调用预览接口
5.1 onlinePreview 接口的 URL 编码规则与生成逻辑
kkfileview 的核心接口只有一个:/onlinePreview。参数url必须是“文件原始 URL 的 Base64 编码”,并且整个 Base64 字符串要做 URL 编码。为什么这么设计?因为文件 URL 可能带查询参数、中文、特殊字符,直接拼进链接会乱,Base64 能保证安全,但 Base64 本身包含+、/、=,这些在 URL 里有特殊含义,所以还要再 URL 编码。顺序是:原始文件 URL -> UTF-8 字节 -> Base64 -> URL 编码 -> 拼到onlinePreview?url=后面。
举个例子,文件 URL 是https://oss.example.com/合同 2024.docx。先 UTF-8 编码,Base64 得到一串字符,再用encodeURIComponent处理,得到最终参数。后端收到后,先 URL 解码,再 Base64 解码,还原出文件 URL。如果中间少了一步,就会预览失败。很多新手直接Base64.encode(fileUrl)拼上去,结果文件 URL 里没有特殊字符时能看,一遇到带+的 URL 就失败。这个坑非常典型。
另外,kkfileview 还支持传其他参数,比如fullfilename指定文件名,watermarkTxt指定水印文字,officePreviewType指定预览方式。这些参数也要 URL 编码。比如你想强制用图片方式预览 Office,可以加&officePreviewType=image。但注意,参数多了链接会很长,浏览器和 Nginx 都有 URL 长度限制,一般 8KB 以内没问题。
5.2 前端集成示例:Java、Python、JavaScript 生成预览链接
实际集成时,你需要在后端生成预览链接,返回给前端。下面是三种语言的示例。
Java:
import java.net.URLEncoder; import java.util.Base64; import java.nio.charset.StandardCharsets; public class PreviewUtil { public static String buildPreviewUrl(String fileUrl) throws Exception { String base64 = Base64.getEncoder() .encodeToString(fileUrl.getBytes(StandardCharsets.UTF_8)); String encoded = URLEncoder.encode(base64, "UTF-8"); return "http://preview.example.com/onlinePreview?url=" + encoded; } }Python:
import base64 import urllib.parse def build_preview_url(file_url): b64 = base64.b64encode(file_url.encode('utf-8')).decode('utf-8') encoded = urllib.parse.quote(b64, safe='') return f"http://preview.example.com/onlinePreview?url={encoded}"JavaScript:
function buildPreviewUrl(fileUrl) { const b64 = btoa(unescape(encodeURIComponent(fileUrl))); const encoded = encodeURIComponent(b64); return `http://preview.example.com/onlinePreview?url=${encoded}`; }注意 Java 的URLEncoder.encode会把空格编成+,而 URL 标准里空格应该是%20。有些场景下+会被后端当成空格,导致 Base64 解码失败。保险做法是编码后再把+替换成%20。Python 的quote默认不编码/,但 Base64 里可能有/,所以要用safe=''强制编码。JavaScript 的btoa不能直接处理中文,要先encodeURIComponent再unescape转成 Latin-1。这些细节不处理好,预览就会时好时坏。
5.3 文件流、远程 URL、本地路径三种调用方式
kkfileview 支持三种文件来源。第一种是远程 URL,最常见,文件放在 OSS、MinIO、Nginx 静态目录,只要 kkfileview 能访问到就行。第二种是文件流,适合文件不公开、需要鉴权的场景,但 kkfileview 原生接口主要接收 URL,如果要传流,需要改源码或者先把文件传到 kkfileview 能访问的临时地址。第三种是本地路径,比如file:///data/files/test.docx,但出于安全考虑,官方默认可能禁用,需要配置file.local.enabled=true并限制目录。
生产环境我推荐远程 URL 方式。业务系统上传文件到对象存储,生成一个带时效的签名 URL,再把这个 URL 传给 kkfileview。这样 kkfileview 不需要存储文件,也不涉及权限,文件访问控制由对象存储负责。注意签名 URL 的有效期要大于预览时间,否则用户看到一半链接过期,就会加载失败。一般设 30 分钟以上,大文件设 1 小时。
如果文件在私网,kkfileview 和文件服务要在同一网络,或者配置代理。不要直接把内网地址暴露给 kkfileview 的公网实例,容易有 SSRF 风险。可以在 kkfileview 前面加一层网关,校验文件 URL 的域名白名单。kkfileview 本身也支持security.whitelist配置,限制允许访问的域名。这个安全点后面还会讲。
5.4 预览效果调优:缓存、水印、禁止下载、过期清理
预览效果调优有几个常用开关。缓存能大幅提升二次打开速度,尤其是同一个文件被多次预览时。配置cache.enabled=true,本地缓存用cache.type=default,集群用cache.type=redis。缓存时间cache.timeout可以设 3600 秒,避免文件更新后一直看旧版。如果文件经常变,缓存时间设短一点,比如 300 秒。
水印配置watermark.enabled=true,watermark.txt填文字。但默认水印是平铺的,位置和透明度可以在源码里调。禁止下载可以通过office.preview.type=image让 Office 转成图片,用户无法直接下载原文件;PDF 也可以转图片。但这样会损失文字可选性。如果既要禁止下载又要保留文字,只能在前端加遮罩,防君子不防小人。
过期清理很重要。file.dir下的临时文件如果不清理,磁盘很快满。除了定时任务,kkfileview 也有file.clean.timeout之类的参数,但不同版本可能不一样,最好自己控制。我通常写一个脚本,每天凌晨 2 点删除 3 天前的文件。同时监控磁盘使用率,超过 80% 告警。Redis 缓存也要设过期时间,避免内存无限增长。
6. 常见问题与排查技巧实录
6.1 预览乱码、字体缺失、中文不显示
中文乱码是最高频的问题。表现是 Word 转 PDF 后中文变成方框或问号。根因是服务器缺少中文字体。排查步骤:进入容器或服务器,执行fc-list :lang=zh,如果没有输出,说明没装中文字体。解决:安装fonts-wqy-zenhei或拷贝字体到/usr/share/fonts,然后fc-cache -fv。Docker 部署时,可以自己写 Dockerfile 把字体 COPY 进去,或者用-v挂载字体目录。
如果装了字体还是乱码,检查 LibreOffice 是否识别。执行libreoffice --headless --convert-to pdf test.docx,看生成的 PDF 是否正常。如果命令行正常但 kkfileview 乱码,可能是 kkfileview 用的字体目录和系统不一致,检查office.home下的字体配置。另外,某些特殊字体(如宋体、黑体)有版权,Linux 上可以用思源字体替代,但替换后排版可能略有差异。
还有一个隐蔽问题:文件本身编码。有些 txt 文件是 GBK 编码,kkfileview 默认按 UTF-8 读,就会乱码。可以在预览参数里指定编码,或者转成 UTF-8 再上传。这个场景在老旧系统迁移时很常见,遇到 txt 乱码先确认文件编码。
6.2 转换失败、进程卡死、内存溢出
转换失败的表现是页面一直转圈,最后报“转换失败”或超时。先看日志,搜索convert或soffice。常见原因:LibreOffice 进程卡死、文件损坏、磁盘满、内存不足。如果是进程卡死,执行ps -ef | grep soffice看有没有残留进程,有就 kill 掉,然后重启 kkfileview。长期方案是设置转换超时,office.convert.timeout=120,并限制并发数。
内存溢出表现为容器被 OOM Killer 杀掉,或者 Java 抛OutOfMemoryError。解决:调大 JVM 堆-Xmx4g,但不要超过物理内存的 70%;限制同时转换数office.convert.max-threads=4;定期重启容器。如果单个文件特别大,比如 200MB 的 Excel,LibreOffice 可能直接崩溃,这种只能提前限制文件大小,或者在业务层拒绝。
我遇到过最诡异的一次是转换特定 PPT 时卡死,日志没有任何错误。后来用命令行单独转也卡死,确定是 LibreOffice 的 bug,升级版本后解决。所以遇到无法解释的转换失败,先确认 LibreOffice 版本,尽量用官方推荐版本。
6.3 远程文件 403/404、跨域与反向代理
预览报 403,说明 kkfileview 请求文件 URL 被拒绝。检查文件 URL 是否带签名、签名是否过期、kkfileview 服务器 IP 是否在白名单。报 404,说明文件 URL 写错或文件不存在。如果是内网 URL,确认 kkfileview 能否 ping 通、能否 curl 到。可以在容器内执行curl -I 文件URL测试。
跨域问题通常不是 kkfileview 本身,而是 iframe 嵌入的页面和父页面不同源。kkfileview 默认允许跨域,如果还有问题,检查 Nginx 是否加了X-Frame-Options或 CSP。反向代理常见问题是路径重写导致url参数被二次编码。解决:Nginx 配置里不要对onlinePreview做 rewrite,直接proxy_pass到后端。如果必须重写,确保url参数原样透传。
还有一个坑是 HTTPS 混合内容。父页面是 HTTPS,预览 iframe 是 HTTP,浏览器会拦截。解决:给 kkfileview 也配 HTTPS,或者用 Nginx 统一入口。证书可以用 Let's Encrypt,配置不复杂。
6.4 性能调优与安全加固(SSRF、文件类型白名单)
性能调优的核心是缓存和并发控制。开启 Redis 缓存后,同一文件二次预览直接从缓存读,不用再调 LibreOffice。并发控制通过office.convert.max-threads限制,避免 CPU 打满。如果预览量很大,可以部署多个 kkfileview 实例,前面挂 Nginx 负载均衡,共享 Redis 缓存。文件临时目录可以各自本地,但缓存要共享。
安全加固不能忽视。kkfileview 接收一个 URL 并去请求,天然有 SSRF 风险。攻击者可能传入http://169.254.169.254/latest/meta-data/探测云元数据,或者扫描内网。必须配置域名白名单,只允许业务需要的文件域名。kkfileview 有security.whitelist参数,可以配置允许的域名。同时限制文件类型,禁止预览可执行文件、脚本文件,配置file.type.whitelist。还要限制文件大小,防止大文件拖垮服务。
另外,预览接口不要直接暴露公网,最好加认证。可以在 Nginx 层加 token 校验,或者用网关鉴权。kkfileview 本身没有复杂的权限体系,它更适合作为内网服务。如果必须公网访问,一定要加 HTTPS、限流、白名单、日志审计。
7. 生产环境经验:高可用与监控
7.1 多实例部署与 Redis 缓存
单机 kkfileview 在预览量上来后容易成为瓶颈。多实例部署是标准方案:部署 2 到 4 个容器,前面用 Nginx 负载均衡,Redis 作为共享缓存。注意几个点:所有实例的application.properties要一致,尤其是 Redis 地址和缓存前缀;文件临时目录各自本地即可,因为缓存命中时不需要文件;Redis 要设置密码和持久化,避免缓存丢失后大量回源转换。
Nginx 负载均衡可以用upstream配置,健康检查可以用max_fails和fail_timeout。如果某个实例挂了,Nginx 自动剔除。kkfileview 没有内置健康检查接口,可以配置一个简单的/路径检查,返回 200 即可。多实例下,水印和预览参数要保持一致,否则用户刷新后可能看到不同效果。
Redis 缓存 key 的命名要避免冲突。kkfileview 默认用文件 URL 的 MD5 或类似方式做 key,一般不会冲突。但如果多个业务共用 Redis,最好加前缀,在配置里改cache.prefix。缓存过期时间根据文件更新频率设置,合同类文件可以长一点,日志类文件短一点。
7.2 日志监控与文件清理
日志是排障的生命线。生产环境要把日志输出到文件,并配置切割。用 logback 按天切割,保留 30 天。同时接入监控系统,比如 Prometheus + Grafana,监控 JVM 内存、CPU、转换队列长度、失败率。kkfileview 暴露的指标有限,可以在 Nginx 层统计请求量和响应时间。如果失败率突然上升,第一时间看日志里的convert error。
文件清理要自动化。file.dir下的临时文件会越积越多,写一个定时脚本,每天清理 3 天前的文件。同时监控磁盘,超过 80% 告警。Redis 缓存也要监控内存,避免打满。如果发现缓存命中率很低,检查缓存配置是否生效,或者文件 URL 是否每次都带不同签名,导致 key 变化。签名 URL 做缓存 key 时,最好去掉签名参数,只保留文件路径,否则缓存命中率会很低。
7.3 与业务系统的解耦建议
kkfileview 最好作为独立服务,不要和业务系统混部。业务系统通过 HTTP 调用,不直接依赖它的 jar 包。这样升级、扩容、排障都互不影响。文件存储也解耦,业务系统把文件存到对象存储,kkfileview 只负责预览。预览链接由业务后端生成,前端只负责 iframe 展示。
如果要做权限控制,可以在业务后端生成一次性预览 token,kkfileview 前面加一层网关校验 token。或者把文件 URL 做成带时效的签名 URL,kkfileview 请求时校验签名。这样既安全又不用改 kkfileview 源码。我现在的做法是:业务后端生成预览链接,链接里带一个短效 token,Nginx 的 Lua 脚本校验 token 后转发到 kkfileview。这样 kkfileview 完全不感知权限,维护简单。
最后分享一个我个人用了很久的部署习惯:Docker 镜像固定版本,配置文件挂载并纳入 Git,字体提前打进镜像,Redis 必开,Nginx 加超时和限流,定时清理临时文件。这套组合跑了两年,除了偶尔升级 LibreOffice,基本没出过大事。kkfileview 不是银弹,但在这个需求区间里,它确实能帮你省下大量造轮子的时间。如果你准备上生产,先把字体和时区搞定,再调缓存和并发,最后加安全和监控,基本就稳了。