使用word模板生成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的方式,上述外用的链接如有侵权请联系我。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值