Aspose 小语种渲染有误
背景
客户反馈系统打印出来的 PDF 中的内容与他们提供的 Word 模板差异很大,差异主要体现在缅甸语上,经过仔细地对比,发现确实有一部分缅甸语语句完全不同。为了修复这个问题废了很大的劲,故在此做一个记录。
流程梳理
根据代码分析,流程大致下图所示:
flowchart TD
Start([开始]) --> loadTemplate[读取 Word 模板]
loadTemplate --> loadRenderParams[读取渲染参数]
loadRenderParams --> poiTlRender[poi-tl 根据模板和参数渲染]
poiTlRender --> asposeConvertPDF[Aspose 将渲染好的模板转换为 PDF]
asposeConvertPDF --> End([结束])
问题分析
根据上述流程图可以怀疑三个地方:
- 读取到的 Word 模板有误:读取到之后又马上输出的内容与模板一致,排除。
- poi-tl 渲染有误:将渲染好的 Word 输出到本地,发现内容与模板一致,排除。
- Aspose 转换时有误:上述操作都确认之后基本上就可以确定是 Aspose 有问题了,因为只有它才会再对二进制输入流进行操作。
第一次尝试解决
既然是 Aspose 的问题,那么就去翻阅官方文档尝试解决。很遗憾的是,翻遍了官方文档,渲染的每一个参数都进行了尝试,问题还是没有得到解决。将问题向主管反馈之后,她提到我们实际上是 Aspose 的付费用户,是可以要求他们提供一定的支持。果不其然,将问题描述给官方之后,官方发了一个补充的 Jar 包过来,将补充的 Jar 包导入项目并按照官方的指引导入对应的配置之后,问题得到了解决并且测试环境也验收通过。
然而事与愿违,在进 UAT 时突然文档打印功能全线瘫痪,根据错误信息定位到罪魁祸首正是这个补充的 Jar 包,抛出的错误是:UnstaisfiedLinkError:'long com.aspose.words.shaping.harrfbuzz.HB.hb_buffer_create(int)'。根据报错信息定位到对应的方法,发现对应的方法有 Native 修饰,这个修饰词在 Java 意味着这段方法是引用的外部链接库,而报错信息翻译过来就是找不到对应的外部连接库,但又仔细翻阅了这个 Jar 包,发现确实是有对应的外部链接库并且本地和测试环境都不会报错,线索在几乎面临中断了。
第 N 次尝试解决
经过分析可以很明确的一点是:UAT 环境报错是因为找不到对应的外部链接库。但奇怪的是 UAT 环境报错,TEST 环境不报错,这两个环境到底有什么不同?
很遗憾的是,经过一番完整的对比,即使深入到了构建脚本和操作系统也没有发现有什么决定性的不同。不过值得一提的是,UAT 环境是客户提供的服务器资源,供应商是 AWS,TEST 环境是公司自己采购的服务器资源,供应商是华为云。
至此好像线索又断了,但这个问题又不能不修,在客户的角度上来说,这个问题还挺致命的。就只能接着采用控制变量法慢慢尝试,由于我当时获取到的服务器权限非常大,UAT 环境肯定是不能经常启停的,故自己写了一个 Mock 仓库,采用相同的构建脚本来搭建。幸运的是,问题得到了复现,不幸的是,经过两天的尝试还是颗粒无收。
不过总体上来说还是有一定的进展的,当时我自己也有一个服务器资源,发现对于这种外部连接库文件,Jar 包启动时会尝试往 Temp 文件夹中放入外部链接库文件,并在启动好之后使用。UAT 环境最大的问题是,无论启动多少次,Temp 文件夹中都不会有外部连接库文件。
终于,在某一天晚上 11 点的时候灵光乍现,由于控制变量法在各种不同的 Linux 操作系统做切换,有些权限不够高的需要输入 sudo 来执行命令,就尝试在 UAT 环境启动 Mock 仓库之前加入 sudo,果不其然,问题得到了解决。
总结
整个过程其实拉得很长,从头到尾几乎用掉了 5 个工作日,期间各种资料都查阅了,与部门的架构也一起探讨过这个问题好几次,还好最终找到病根并根除了。造成这个问题的罪魁祸首就是不同的供应商对于服务器初始用户的权限控制不同,国内大多数供应商给的权限都比较高,而国外的供应商相反,恰恰 Jar 启动时如果含有外部链接库又需要 Temp 文件夹的写入权限,这才导致了不同环境出现了差异并且很难定位到问题的根本!
不过总的来说还是因为自己对各种资源的特性不是很了解,如果都了如指掌的话,其实也能猜个八九不离十,这恰恰印证了那句玩笑话:艰难的问题的解决方式往往很简单!
当时解决之后就猜测估计不止缅甸语有问题,只要是小语种都有可能出现这个问题,没想到这个猜测在几个月之后在别的项目得到了印证,那个项目的主要语言是阿拉伯语。
