头图

【笑小枫的SpringBoot系列】【十五】SpringBoot根据模板生成Word

通过maven引入需要的依赖

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

创建导出模版

1、首先创建一个word模版文档,模版如下图

SpringCloud江湖

word模版地址:http://file.xiaoxiaofeng.site/files/word.doc

注意word中的占位符的格式,就是freemaker的格式

详细解释如下:

  • 文字处理:

直接用${} 中间为替换的字段名。

如果直接在word里面定义${title},在转换成xml的时候有可能会被一些编码隔开,这个时候只需要用word打开xml,将这些内容重新输入一遍。

强烈建议用IDE打开,然后格式化下,在进行检查处理,能用搜索尽量搜索,xml的格式里面有太多的代码,不要硬刚😂
  • 图片处理:

需要在word文档模版中插入图片

将word转换成xml后,打开xml,会将我们的图片转换成长长的一段base64。

我们把base64换成我们的${pic}就可以了,pic为字段名,可任意替换

  • 列表处理:

需要在word文档模版中插入表格

找到第二个<w:tr>,第一行是我们的标题,在其前面添加 <#list peopleList as list> 其中 peopleList是传入list的集合名称 list 是别名。

参数取值为:${list.name}这样。

在与<w:tr>配对的</w:tr>后面添加</#list>。 语法同freemaker的for循环语法

创建ftl模板

将上述word文档另存为test.xml格式,另存完之后可以用浏览器打开test.xml文件,查看我们的占位符是否标准

SpringCloud江湖

注意:占位符有时候会发生被隔开的情况,如下图:

image-20230319215559562

图片的替换同上面的说明,图片为一串长长的base64,如下图所示:

SpringCloud江湖

然后将文件放置resources/templates目录下。

放置在resource目录下即可,剩下的目录根据自己需求定制。注意要与WordUtil.java中的目录对应。

编写程序

通用的导出工具类

在util包下创建WordUtil.java工具类,👇

package com.maple.demo.util;

import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Map;

import static freemarker.template.Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS;

/**
 * @author 笑小枫
 * @date 2022/7/27
 * @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a>
 */
public class WordUtil {
    private final Configuration configuration;

    public WordUtil() {
        configuration = new Configuration(DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        configuration.setDefaultEncoding("UTF-8");
    }

    public void createWord(Map<String, Object> dataMap, String templateName, String fileName) {
        // 模板文件所在路径
        configuration.setClassForTemplateLoading(this.getClass(), "/templates");
        Template t = null;
        try {
            // 获取模板文件
            t = configuration.getTemplate(templateName, "UTF-8");
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 导出文件
        File outFile = new File(fileName);
        try (Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), StandardCharsets.UTF_8))) {
            if (t != null) {
                // 将填充数据填入模板文件并输出到目标文件
                t.process(dataMap, out);
            }
        } catch (IOException | TemplateException e1) {
            e1.printStackTrace();
        }
    }
}

请求接口

controller包下创建TestWordController.java👇

其中用到了之前文章的文件转base64,参考文章[15.Base64与File互转]()

package com.maple.demo.controller;

import com.maple.demo.util.Base64Util;
import com.maple.demo.util.WordUtil;
import io.swagger.annotations.Api;
import lombok.AllArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.util.*;

/**
 * @author 笑小枫
 * @date 2022/7/27
 * @see <a href="https://www.xiaoxiaofeng.com">https://www.xiaoxiaofeng.com</a>
 */
@RestController
@AllArgsConstructor
@RequestMapping("/example")
@Api(tags = "实例演示-根据模板生成word")
public class TestWordController {
    /**
     * 文字处理:
     * 直接用${} 中间为替换的字段名。
     * 如果直接在word里面定义${title},在转换成xml的时候有可能会被一些编码隔开,这个时候只需要用word打开xml,将这些内容重新输入一遍。
     * 可以用浏览器打开xml,检出自己定义的${}的内容是否都在一起,是否有被编码隔开的情况。
     * 图片处理:
     * 需要在word文档模版中插入图片
     * 将word转换成xml后,打开xml,会将我们的图片转换成长长的一段base64。
     * 我们把base64换成我们的${pic}就可以了,pic为字段名,可任意替换
     * 列表处理:
     * 需要在word文档模版中插入表格
     * 找到第二个<w:tr>,在其前面添加 <#list peopleList as list> 其中 peopleList是传入list的集合名称 list 是别名。
     * 参数取值为:${list.name}这样。
     * 在与<w:tr>配对的</w:tr>后面添加</#list>。 语法同freemaker的for循环语法
     */
    @GetMapping("/exportWord")
    public void exportWord(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 1.创建临时文件夹
        String rootPath = request.getSession().getServletContext().getRealPath("/");
        String fileName = UUID.randomUUID().toString().replace("-", "");

        // 处理图片信息,将图片转为base64字符串
        File imageFile = new File("D:\\xiaoxiaofeng.jpg");
        String base64 = Base64Util.fileToBase64(imageFile);

        // 处理表格的数据信息
        List<Map<String, Object>> list = new ArrayList<>();
        int size = 5;
        for (int i = 0; i < size; i++) {
            Map<String, Object> map = new HashMap<>(16);
            map.put("nickName", "笑小枫" + i);
            map.put("visitTime", "2022-07-27");
            map.put("isLike", "是");
            map.put("isAttention", "是");
            list.add(map);
        }

        // 加载word中的数据信息
        WordUtil word = new WordUtil();
        Map<String, Object> dataMap = new HashMap<>(16);
        dataMap.put("title", "记得关注我哟");
        dataMap.put("email", "1150640979@qq.com");
        dataMap.put("name", "笑小枫");
        dataMap.put("createDate", "2022-07-27");
        dataMap.put("pic", base64);
        dataMap.put("peopleList", list);
        word.createWord(dataMap, "word.xml", rootPath + "/" + fileName + ".doc");

        String exportName = "测试word";
        response.setCharacterEncoding("utf-8");
        response.setContentType("application/msword");
        // 设置浏览器以下载的方式处理该文件名
        response.setHeader("Content-Disposition", "attachment;filename=".concat(String.valueOf(URLEncoder.encode(exportName + ".doc", "UTF-8"))));

        File file = new File(rootPath + "/" + fileName + ".doc");
        try (InputStream fin = new FileInputStream(file);
             ServletOutputStream out = response.getOutputStream()) {
            // 缓冲区
            byte[] buffer = new byte[512];
            int bytesToRead;
            // 通过循环将读入的Word文件的内容输出到浏览器中
            while ((bytesToRead = fin.read(buffer)) != -1) {
                out.write(buffer, 0, bytesToRead);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 删除临时文件
            file.delete();
        }
    }
}

测试

在浏览器输入http://localhost:6666/example/exportWord便可以看到你的word出来了呦。

导出效果图如下:
image-20230319215538427

关于笑小枫💕

本章到这里结束了,喜欢的朋友关注一下我呦😘😘,大伙的支持,就是我坚持写下去的动力。

微信公众号:笑小枫

笑小枫个人博客:https://www.xiaoxiaofeng.com

本文源码:https://github.com/hack-feng/maple-demo

4 声望
1 粉丝
0 条评论
推荐阅读
Java分布式环境下并发编程实践
在Java中,我们可以使用线程来实现并发编程,但是在多线程编程中,我们需要考虑线程安全、锁、死锁等问题。本文将介绍Java中的并发编程,包括线程安全、锁、死锁等内容,同时提供实际的代码案例,让读者更容易理...

笑小枫阅读 606

封面图
Java8的新特性
Java语言特性系列Java5的新特性Java6的新特性Java7的新特性Java8的新特性Java9的新特性Java10的新特性Java11的新特性Java12的新特性Java13的新特性Java14的新特性Java15的新特性Java16的新特性Java17的新特性Java...

codecraft32阅读 27.6k评论 1

一文彻底搞懂加密、数字签名和数字证书!
微信搜索🔍「编程指北」,关注这个写干货的程序员,回复「资源」,即可获取后台开发学习路线和书籍来源:个人CS学习网站:[链接]前言这本是 2020 年一个平平无奇的周末,小北在家里刷着 B 站,看着喜欢的 up 主视...

编程指北71阅读 33.7k评论 20

Java11的新特性
Java语言特性系列Java5的新特性Java6的新特性Java7的新特性Java8的新特性Java9的新特性Java10的新特性Java11的新特性Java12的新特性Java13的新特性Java14的新特性Java15的新特性Java16的新特性Java17的新特性Java...

codecraft28阅读 19.4k评论 3

Java5的新特性
Java语言特性系列Java5的新特性Java6的新特性Java7的新特性Java8的新特性Java9的新特性Java10的新特性Java11的新特性Java12的新特性Java13的新特性Java14的新特性Java15的新特性Java16的新特性Java17的新特性Java...

codecraft13阅读 21.8k

Java9的新特性
Java语言特性系列Java5的新特性Java6的新特性Java7的新特性Java8的新特性Java9的新特性Java10的新特性Java11的新特性Java12的新特性Java13的新特性Java14的新特性Java15的新特性Java16的新特性Java17的新特性Java...

codecraft20阅读 15.4k

Java13的新特性
Java语言特性系列Java5的新特性Java6的新特性Java7的新特性Java8的新特性Java9的新特性Java10的新特性Java11的新特性Java12的新特性Java13的新特性Java14的新特性Java15的新特性Java16的新特性Java17的新特性Java...

codecraft17阅读 11.2k

4 声望
1 粉丝
宣传栏