Java使用freeMarker动态生成复杂word

最近的一个项目需要根据业务,在服务器端根据数据动态生成word,包括填入数据、图片,根据内容控制表格的行数。所以就去网上搜索java操作word的第三方库。然后试了一下,感觉到很麻烦,光是填入数据这个小小的功能,都十分难写。正当一筹莫展之际,看到了网上大佬的方法: 将word转成xml,再使用freeMark对xml文档操作,最后将xml转成word。最后自己看别人的文章实现了下,确实简单方便。大佬文章链接。

下面教大家如何实现:简单数据填充、插入图片、动态插入表格。

下面是我写好了的模板word:

我们现在要做的是,根据数据动态填写姓名、头像图片、成绩。

一、将word文件转成xml文件

不要直接改后缀名,不然打开全是乱码!在保存界面选择“另存为”,然后保存类型选择xml。

二、对xml文件进行处理

使用freemarker语法对xml文档进行处理,然后才能填充。freemarker功能强大,一些逻辑判断都是可以实现的,在此就只讲我们需要用到的语法。先将xml文件拷贝复制进idea中,然后使用idea的格式化功能,就能比较清晰的看到xml文档的结构。

1、首先,我们定位到“我叫小明”这一句:

将其替换:

2、替换图片。图片在xml中以base64的方式存储,当你看到一大段不知所云的字符时你就找到了。

             

       将字符替换为${image}


       3、动态生成表格的操作。<w:tr></w:tr>代表word表格的一行。下面的xml对应word表格中的一行 “数学”  “97”。

<w:tr w:rsidR="00EB70DF" w:rsidTr="00EB70DF">
                            <w:tc>
                                <w:tcPr>
                                    <w:tcW w:w="4148" w:type="dxa"/>
                                </w:tcPr>
                                <w:p w:rsidR="00EB70DF" w:rsidRDefault="00EB70DF" w:rsidP="00EB70DF">
                                    <w:pPr>
                                        <w:rPr>
                                            <w:rFonts w:hint="eastAsia"/>
                                        </w:rPr>
                                    </w:pPr>
                                    <w:r>
                                        <w:rPr>
                                            <w:rFonts w:hint="eastAsia"/>
                                        </w:rPr>
                                        <w:t>数学</w:t>
                                    </w:r>
                                </w:p>
                            </w:tc>
                            <w:tc>
                                <w:tcPr>
                                    <w:tcW w:w="4148" w:type="dxa"/>
                                </w:tcPr>
                                <w:p w:rsidR="00EB70DF" w:rsidRDefault="00EB70DF" w:rsidP="00EB70DF">
                                    <w:pPr>
                                        <w:rPr>
                                            <w:rFonts w:hint="eastAsia"/>
                                        </w:rPr>
                                    </w:pPr>
                                    <w:r>
                                        <w:rPr>
                                            <w:rFonts w:hint="eastAsia"/>
                                        </w:rPr>
                                        <w:t>97</w:t>
                                    </w:r>
                                </w:p>
                            </w:tc>
                        </w:tr>

只需在这一段最外层,包上循环语句<#list grades as grade></#list>,然后将“数学”改为${grade.courseName},“97” 改为${grade.score}。

<#list grades as grade> //添加
<w:tr w:rsidR="00EB70DF" w:rsidTr="00EB70DF">
                            <w:tc>
                                <w:tcPr>
                                    <w:tcW w:w="4148" w:type="dxa"/>
                                </w:tcPr>
                                <w:p w:rsidR="00EB70DF" w:rsidRDefault="00EB70DF" w:rsidP="00EB70DF">
                                    <w:pPr>
                                        <w:rPr>
                                            <w:rFonts w:hint="eastAsia"/>
                                        </w:rPr>
                                    </w:pPr>
                                    <w:r>
                                        <w:rPr>
                                            <w:rFonts w:hint="eastAsia"/>
                                        </w:rPr>
                                        <w:t>${grade.courseName}</w:t> //改为占位符
                                    </w:r>
                                </w:p>
                            </w:tc>
                            <w:tc>
                                <w:tcPr>
                                    <w:tcW w:w="4148" w:type="dxa"/>
                                </w:tcPr>
                                <w:p w:rsidR="00EB70DF" w:rsidRDefault="00EB70DF" w:rsidP="00EB70DF">
                                    <w:pPr>
                                        <w:rPr>
                                            <w:rFonts w:hint="eastAsia"/>
                                        </w:rPr>
                                    </w:pPr>
                                    <w:r>
                                        <w:rPr>
                                            <w:rFonts w:hint="eastAsia"/>
                                        </w:rPr>
                                        <w:t>${grade.score}</w:t> //改为占位符
                                    </w:r>
                                </w:p>
                            </w:tc>
                        </w:tr>
</#list> //添加

至此,xml文件的处理完成。接下来进入代码编写阶段。

三、代码编写

首先要导入freemarker的依赖包。

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-freemarker</artifactId>
      <version>2.1.2.RELEASE</version>
    </dependency>

测试代码编写:

public class FMarker {
    public static void main(String[] args) {
        HashMap<String, Object> map = new HashMap<>();
        DocumentHandler dh = new DocumentHandler();
        Template t = dh.getTemplate();
        Writer out = dh.getWriter();
        List<Grade> grades = new ArrayList<>();
        grades.add(new Grade("语文","70"));
        grades.add(new Grade("英语","80"));
        grades.add(new Grade("物理","90"));
        //注意 变量名要与xml中占位符保持一致
        map.put("name","小红");
        map.put("grades",grades);
        map.put("image",dh.getImageStr());
        dh.createDoc(t, map, out);
    }
}

class DocumentHandler {
    private Configuration configuration = null;

    public DocumentHandler() {
        this.configuration = new Configuration();
        this.configuration.setDefaultEncoding("utf-8");
    }

    //将xml文件转成模板
    public Template getTemplate() {
        configuration.setClassForTemplateLoading(this.getClass(),
                "/com/test/word");
        Template t = null;
        try {
            t = configuration.getTemplate("Doc1.xml");//模板xml路径
            t.setEncoding("utf-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
        return t;
    }

    //获得输出流对象
    public Writer getWriter() {
        //填充后,新文件路径
        File outFile = new File("D:\\ideacode\\uploadServlet\\src\\main\\java\\com\\test\\word\\newDoc1.doc");
        Writer out = null;
        try {
            out = new BufferedWriter(new OutputStreamWriter(
                    new FileOutputStream(outFile), "utf-8"
            ));
        } catch (UnsupportedEncodingException | FileNotFoundException e) {
            e.printStackTrace();
        }
        return out;
    }

    //转化生成word文件
    public void createDoc(Template t, Map dataMap, Writer out) {
        try {
            t.process(dataMap, out);
            out.close();
        } catch (TemplateException | IOException e) {
            e.printStackTrace();
        }
    }

    //将图片转化成BASE64
    public String getImageStr(){
        //新图片路径
        String imageFile = "D:\\ideacode\\uploadServlet\\src\\main\\java\\com\\test\\word\\newImage.jpg";
        InputStream in = null;
        byte[] data = null;
        try{
            in = new FileInputStream(imageFile);
            data = new byte[in.available()];
            in.read(data);
            in.close();
        }catch (IOException e){
            e.printStackTrace();
        }
        BASE64Encoder base64Encoder = new BASE64Encoder();
        return base64Encoder.encode(data);
    }
}

项目结构:

Doc1.xml就是预先准备的word模板

DocumentHanlder是核心操作类

FMarker是来写测试函数的

Grade类就两个成员变量:courseName,score。我就不写出来了。

newDoc1.doc是生成的word文档

newImage是准备插入的图片

最终结果:

而且不知道大家注意到没有,我们填充的“小红”也变成了红色字体,我们插入的图片,大小也是保持和原来一样!其实原理很简单,word在转成xml时,关于格式的控制信息也随之写入xml里,我们只是改了内容,而没改格式。所以后填入的数据格式会和之前的数据保持一致。这样,我们只要预先设置好模板的格式,就能动态生成自己想要的格式的文档!其次freemarker的语法功能还远不止这些,有兴趣的同学可以去看一下。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值