使用word模板生成word文档的各类方案

  • 生成word的各种方案
    • word另存xml进行后续处理
      • 2003版本word(.doc)的xml处理并生成word
      • 2007版本word(.docx)的xml处理
    • poi直接操作word对象进行文本替换生成word
    • 使用poi-tl库对模板进行动态生成word文档(本人的最终方案)
    • 综合方案评价

生成word的各种方案

项目要求可以从一个word文档里,定好word的模板,然后后台读取数据后生成pdf并提供下载。本文章着重讲word模板到生成word文档的部分,由于自己也是刚接触这个内容,欢迎纠错,并且网上有好几种方式,在这里我都会讲个人的观点。
当前项目要求:
1、word模板是可变的,即更换模板,尽量不改变代码。
2、保留word模板本身样式,即本身字体颜色图片大小等样式保留。
3、跨平台,毕竟项目是要部署在linux上的。
4、可以实现超链接(网址url或者)
一、由word转xml编写key后另存ftl格式,后台代码用map进行填充生成word文档
1、优点:
保证稳定性,这种方式一定完成效果。保留字体样式等效果。
2、缺点:
①过程复杂,并且word转xml会导致部分文字被拆分多个标签,需要先做xml解析合并相同的xml标签后才能进行填充。即需要多做一个xml解析的功能。
②不可动态扩展表格,我的项目想根据传过来的数据进行动态拓展表格,其实也不是不可以,只需要在xml解析部分中将单元格部分的标签复制就好了,但是这明显会增加工作量。
③版本兼容问题,2003(.doc)和2007(.docx)版本,在这个方式下完全不同,需要做不同版本的写法,或者要求版本不变。
备注:ftl模板完成一次后即可反复利用,所以如果没有更换模板的需求下,xml合并部分和编写key部分可以人工完成,这样就会省下许多工作量。
二、直接利用poi等工具读取word文档自己定义的key替换相应的值
1、优点:
工作量小
2、缺点:
①不太严谨,毕竟key是自己在文本定义的,本质就是文本替换,只是做了特殊标记而已,难免可能出现不是用于标记的但是却被替换的情况。
②替换图片和超链接困难(具体实施情况本人并没有细探究)

word另存xml进行后续处理

比较多人使用的方式是word另存xml,然后在xml里做key的标记,另存成freemarker的.ftl格式文件,在代码里用map的方式根据key的不同去赋值。这里会有个问题,因为word文档是分.doc和.docx格式,即2003和2007版本。

2003版本word(.doc)的xml处理并生成word

测试例子:

如图所示,本人需要替换的是两行文本以及一个表格,一张图片,一个指向网站的超链接,一个指向一个excel文件的超链接。
另存2003版本的xml格式,进行编辑。

可见2003版本xml的结构,其中fonts标签记录了字体库,styles记录了样式,在body标签里记录了文本主体信息。

可见${name1}仍然会被拆分成2个<w:r>标签,虽然本人是先在文档里写key再生成xml去查看,实际上先写成实际文本信息再在此处替换成key,一样会被拆分,尤其是英文和中文和符号三类相连的部分会被断开。此时需要合并同样样式的<w:t>标签,使key完整。如果有更换模板的需求,就得写一个xml解析的功能,满足合并<w:t>标签的功能。

关于图片处理:

<w:binData>记录了图片的base64信息,因此代码中实际需要替换的是这串base64信息。
<w:binData w:name=“wordml://02000001.jpg” xml:space=“preserve”>${data}</w:binData>
将base64信息用key去标记。

关于超链接处理:


可见,实际的url是在w:dest=“http://www.baidu.com"或w:dest=“C:\Users\lenovo\Desktop\test.xlsx"里。将引号里的url标记成key即可。即<w:hlink w:dest=”${url}”>注意保留引号。
完成后另存ftl后缀名即可。

public Map<String,Object> completeData(Map<String,Object> map)throws Exception{try{map.put("name1","666");map.put("name2","777");map.put("title1","666title1");map.put("title2","66title2");map.put("title3","66title3");map.put("pro1","999");map.put("pro2","1000");map.put("pro3","1001");File file=new File("C:\\Users\\lenovo\\Desktop\\pdftest\\firstpdf\\src\\main\\resources\\static\\test.jpg");String imageBase64 = encodeBase64File(file);map.put("data",imageBase64);map.put("github","http://www.github.com");map.put("excel","test.xlsx");}catch(Exception e){logger.error("ProjectController.completeData error",e);throw e;}return map;}
    /*** 将文件转化为Base64编码* @param file* @return* @throws Exception*/public static String encodeBase64File(File file) throws Exception {FileInputStream inputFile = new FileInputStream(file);byte[] buffer = new byte[(int) file.length()];inputFile.read(buffer);inputFile.close();return new BASE64Encoder().encode(buffer);}
   /*** 修改文件模板* @param fileName* @param map* @return* @throws Exception*/public File writeFileTemplate(String fileName,Map<String,Object> map) throws Exception{try{Configuration configuration=new Configuration();configuration.setDefaultEncoding("utf-8");String tempPath="src/main/resources/";String templatePath="src/main/resources/templates/";configuration.setDirectoryForTemplateLoading(new File(templatePath));Template template=configuration.getTemplate("model.ftl");String outPath=tempPath+fileName;File outFile=new File(outPath);Writer writer=null;try{writer=new OutputStreamWriter(new FileOutputStream(outFile), "utf-8");template.process(map,writer);}catch(Exception e){throw e;}finally{writer.close();}return outFile;}catch(Exception e){throw e;}

核心思路就是读取ftl文件,读取map的值填入模板中生成word。具体完整代码网上很多,这里不再赘述。

2007版本word(.docx)的xml处理

之所以分版本说明,是因为两个文件格式的结构不同。
上述xml的方式生成的文档只能是.doc,将生成的文件名改成.docx将打不开文件。
原因:将.dox文件后缀名改为.zip可以发现,本质是一个压缩包

打开word文件夹

可以发现文档的主要信息都在这里,并且页眉页脚字体样式什么的都分了不同的xml进行存储。

图片则存储在了media文件夹中,不需要base64码了。
这就是为什么网上主流的方式都是.doc,因为.docx的格式过于复杂。虽然document.xml的更改方式与.doc版本的方式是一样的。
但是代码功能复杂了,即还需要一个压缩与解压的功能,因此本人并没有细究下去,而且也没找到超链接的修改位置,可能方式变了我没找到,于是直接弃用。

poi直接操作word对象进行文本替换生成word

来自简书的一个文档:POI操作word模板并生成新的word.docx
如有侵权,请联系。
核心思路网上大同小异,都是利用poi的功能去找到自己要替换的位置然后进行替换,本质就是文本替换。缺点就是代码会十分不灵活,因为对于表格部分处理会变得十分复杂且不够灵活动态。优点就是工作量比较小。
备注:.doc和.docx的方式同样不同,但是思路一样

使用poi-tl库对模板进行动态生成word文档(本人的最终方案)

poi-tl是国人对poi库的再次封装的一个库,特点是针对模板化word文档改良的,在github上已经有1k的star了
此处奉上相关链接
poi-tl的官方地址
poi-tl的github地址


由作者github提供的示例图可以看出,poi-tl可以十分灵活的在多个地方用规范的key进行标记,包括图片、表格、文档、文本等信息都可以以一个key标记。


此处以官方提供的示例demo为例。

对比可以看出,文章、带编号的段落、表格、图片、超链接,都可以以key的方式去标记。不同类型的用不同的标记,代码也是十分简洁,只需要读取模板文档然后输入一个map就完成了。实际上不仅仅是map,有不同属性的一个完整的对象也是可以作为输入参数的。此处不附代码了,毕竟已经贴了几个作者的原图,此处仅以宣传作用,若有侵权,请联系。详细代码作者github上有完整的,或者从官网里看。

优点:模板可以满足所有要求,即超链接、保留样式、动态拓展表格、跨平台(因为基于poi)、代码工作量小、制作模板工作量小,支持.docx文件
缺点:表格样式需要代码去写,不能用word模板里的表格样式去动态拓展(或者本人暂时没研究出来)、由于几乎全是key,所以不方便给制作模板的人有预览的效果,即几千字的文章,哪怕有分段等操作,在poi-tl中,也是一个key就完成了,对于操作的人来说工作量小,但是不方便想象实际效果。

综合方案评价

这里借助poi-tl的官方的一个表格图

上述xml的方式即是freemarker的方式,简书作者的则是poi的方式,我最终采取的是poi-tl的方式,上述外用的链接如有侵权请联系我。

使用word模板生成word文档的各类方案相关推荐

  1. word模板生成word报表文档

    主要功能为根据word模板生成word报表文档,注意引用Interop.Word.dll; 首先要生成word程序对象 Word.Application app = new Word.Applicat ...

  2. C#根据word模板生成word表格报表文档

    主要功能为根据word模板生成word报表文档,注意引用Interop.Word.dll; 首先要生成word程序对象 Word.Application app = new Word.Applicat ...

  3. JAVA实现模板word文档导入,Java依据word模板生成word文档之后台解析和实现及部分代码(一)...

    Java根据word模板生成word文档之后台解析和实现及部分代码(一) 后台主要工作是解析XML定义的标签文件,并获取到数据集,放入到Map中,然后调用Jacob.jar中提供的相关方法来实现替换. ...

  4. PHP 使用word模板生成word文档示例

    <?php namespace Home\Controller; use PhpOffice\PhpWord\TemplateProcessor; use Think\Controller; c ...

  5. 使用java Apache poi 根据word模板生成word报表

    使用java Apache poi 根据word模板生成word报表 使用poi读取word模板,替换word中的{text}标签,并根据自定义标签循环生成表格或表格中的行. 代码示例下载:https ...

  6. java 根据word模板生成word文件

    Java可以使用Apache POI库来生成Word文件,并且也可以使用freemarker等模板引擎来实现根据Word模板生成Word文件的功能. 下面是一个简单的示例代码,可以帮助您快速入门. 模 ...

  7. Springboot通过模板生成pdf文档带图片

    Springboot通过模板生成pdf文档带图片 环境:springboot2.3.10.RELEASE + itextpdf5.5.13.2 依赖 <dependency><gro ...

  8. apache poi使用例_使用java Apache poi 根据word模板生成word报表例子

    [实例简介] 使用java Apache poi 根据word模板生成word报表 仅支持docx格式的word文件,大概是word2010及以后版本,doc格式不支持. 使用说明:https://b ...

  9. 根据word模板生成word和PDF

    根据word模板生成word和PDF 需求:有一个固定的合同模板,在vue前台填写指定的信息,替换合同模板指定的内容 我们使用的默认模板内容如图: 我们在前端填写的字段就是合同名称.项目名称和项目金额 ...

最新文章

  1. pandas loc iloc(self index location 即按照行索引来获取数据)
  2. Oculus也陷隐私门:向Facebook发送隐私数据
  3. Android深度探索(卷1)HAL与驱动开发--读书笔记(第一章)
  4. mysql 分表全局id_MySQL分庫分表環境下全局ID生成方案
  5. 这些令人仰望的C++大咖,都是怎样炼成的?
  6. (转) android UI进阶之用gallery实现可滑动的Tab
  7. LuaTinker向Linux移植成功
  8. 计算机科学导论内容大纲,《计算机科学导论》大纲
  9. 政企数字化转型怎么做?先从华为云WeLink “云签约”打个样儿
  10. jmeter 前置处理器
  11. 有哪些「饥饿营销」的失败案例?
  12. 线程的6种状态(NEW,RUNNABLE,BLOCKED,WAITING,TINED_WATING,TEMINATE)
  13. 在win10 系统输入法 输入几下老是自动被禁用解决办法
  14. 2012年度x86服务器虚拟化基础设施魔力象限,五载解读!Gartner x86虚拟化魔力象限...
  15. Android微信开发者平台更换包签名后,微信登录提示签名不对
  16. (三)RabbitMQ集群(Ⅰ)
  17. 干货 | 什么是高频电解电容,它有普通电解电容有什么区别?
  18. 外贸盒子H96MAX_S905X3_1000M-唯一默认中文版-安卓9.0-官改精简线刷包及教程-202208
  19. *srv.exe蠕虫病毒打开exe程序弹浏览器窗体的解决方案
  20. android wifi热点默认名称,Android WIFI热点默认SSID的修改方法

热门文章

  1. 计算机如何连接发票打印机,惠普打印机怎么连接电脑详细步骤,发票打印机怎么添加-...
  2. 能力素质有所欠缺_学有所思,思有所悟,悟有所行
  3. LabVIEW代码中常见的错误
  4. Arduino(五)——呼吸灯
  5. 微信表情的字符编号完整版【图文并茂哦!】
  6. JS面试题汇总(八)
  7. HTML有哪些浏览器支持,哪些浏览器支持 HTML5?
  8. 牧码客(卢益贵):专业取名软件——吉名宝
  9. Git上传代码报错Push rejected: Push to origin/master was rejected
  10. 6:把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转。 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素。 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转