记一次word转pdf的开发过程及思考


需求介绍

因付费客户需要,要实现一个批量生成商务文件pdf的功能,客户通过excel文档导入业务数据,要求根据业务数据的编号将不同数据分组,每一组的数据填充生成一个文件pdf,pdf的模板样式由客户提供word版本,最终将所有生成的pdf打包成一个zip,供客户下载使用

开发过程

1.项目中之前制作pdf的方式是jasperReport+itextpdf,需要手动绘制pdf模板和指定表单,缺点是表格数据的扩展性和样式改动麻烦,很难与word样式一致,表格行数不可变。在调研之后决定改为使用word模板填充数据,再转为pdf的方案,使用了开源工具poi-tl+xdocReport,poi-tl将数据渲染到word模板中生成有数据的docx文件,再通过xdocreport将docx转为pdf,这个方案保留了原模板的样式,且展示内容会根据数据长度和个数自动扩展

2.发现docx转pdf后,文档中的二维码图片丢失,百度后没有找到合适的方案,最终选择了一种麻烦的方法,生成pdf后,通过itextpdf将图片动态添加到pdf的指定坐标位置,多了一次io,但暂时只能用这种方式解决

3.自测后发现docx转pdf的过程耗时较长,开发机器上平均每一份转换耗时2-3秒左右,一百份就需要3-4分钟,用户体验较差。由于转换是xdocreport内部封装的方法,只能考虑用多线程同时生成多个pdf,测试环境及线上服务器是八核cpu,线程池core Size为9,为了不阻塞其他业务同时将时间尽量控制在一分钟以内,从线程池中取4个线程,将原数据分为4组,同时生成pdf,调整后性能提高了一倍左右(可能是开发环境cpu核心数、io瓶颈导致没有提升4倍)

4.改为多线程制作文件后,出现了部分pdf大小为0,制作失败的情况,经排查是二维码添加失败,项目中生成二维码的工具默认文件名是毫秒时间戳,多线程执行时可能会重复生成,被某个线程使用后删除,其他线程无法获取,导致生成失败。修改方案是每个线程单独建一个名称是线程id的文件夹,生成二维码放在对应的文件夹中,避免了冲突。(测试环境没有测出大小为0的场景,原因应该是本地多线程由于cpu核心数较少,使用了时间片轮换,才出现了一个线程删除后另一个线程使用的场景,如果在不同cpu核心上执行,同时使用也差不多同时删除,出现概率很低,但仍需要排除此风险)

5.测试环境生成的pdf只有英文不显示中文,通过dockerfile上传windows下的字体文件到linux的usr/share/fonts目录,注意这种方式拷贝文件,不要放在src目录,否则可能编译出错

6.最坑的地方来了TT,使用poi-tl和xdocreport时,没有多做思考,都使用了最新的版本和api,顺带将项目中的poi等依赖版本升级,提测后,部分老功能报错,当前版本的部分类无法兼容老功能。无奈还原老版本后,又发现这次的转换功能各种兼容性报错.....离上线只有三天,只能硬着头皮换方案。尝试了换dox4j,jacob等方案都遇到兼容性问题,最终死马当活马医,逐个尝试xdocreport的所有历史版本,终于找到了一个能兼容老功能依赖版本的,然后poi-tl也必须降版本,写好的api工具类都用不了了,泪千行。样式变形、word填充数据失败、填充后的docx转换报错,只能加班才一个个解决这些坑爹问题

问题思考

开源工具使用问题。开源工具使用不当导致了本次需求开发多了很多时间和精力上的消耗,由于开源工具可能经历多个不同的公司和团队维护,因此历史兼容性较差。所以引入不熟悉的开源工具,一定要首先考虑它依赖的其他组件是否与项目存在版本冲突,能否兼容之前的版本功能?确认兼容性之后,再熟悉api和使用,否则急着使用可能后期会前功尽弃,推倒重来

功能的开发过程中至少要考虑三个角度:需求点满足+时间性能满足+风险和潜在性问题考虑。例如本次开发过程中,工具的选择满足了需求点,多线程提高了时间性能,但是方案都有各自的坑,再加上开发机器和线上机器操作系统的不一致,出现了很多意料之外的问题,所以写代码一定不能想当然和过于随意,尽可能设计好可用性强的方案之后再展开具体工作