之前项目中有一个生成word文档的功能,根据数据库的数据进行word文档的生成,当时实现了该功能,但没有打笔记忘记了,特此打个笔记记录一下
我使用的freemarker的技术来实现word的生成,网上也都比较推荐该方式,他是通过根据一个模板,进行填充的方式实现word的生成
我们要使用freemarker生成word,首先要自己去定义一个doc模板,模板中要填充的数据使用字符串模板的方式定义 ${ 字段名 }
定义完我们的模板之后,我们要将文档保存为xml的格式
保存完的xml文件是一行的不好查看,我们可以提前复制到idea中,使用ctrl + alt + l 进行格式化,就变成标准的xml格式了
保存为xml常见的问题就是${name}被拆开了,我们要进行修改
修改为下面这样(多余的wr直接删除即可)
基础信息的定义只要保证我们转化成的xml文件中的${name} , ${age}格式没错误即可
在xml文件中我们的 <w:tbl> 代表是我们的表格 ,<w:tr> 代表的是我们的行 ,<w:tc> 代表我们的列
我们要想使用表格的方式,肯定大部分是要使用我们的list集合进行数据填充的,我们可以通过下面的方式进行遍历填充数据
我们在表格的表头下的<w:tr>上添加
<#list bookList as book>
在我们</w:tr>下添加
</#list>
其中bookList代表我们要遍历的集合,book代表出来的结果,book.month代表book中的字段
有两个标签
该标签表示,从这个开始进行合并
<w:vMerge w:val="restart"/>
该标签表示,只要带这个标签他就与上面的这个标签合并
<w:vMerge/>
我们将这两个标签定义到我们要合并的单元格的<w:tcPr>中
- <w:tcPr>
- <w:tcW w:w="2765" w:type="dxa"/>
- <#-- 表示居中-->
- <w:vAlign w:val="center"/>
- <#-- 将month 等于1 和 2的进行合并-->
- <#if book.month == 1>
- <w:vMerge w:val="restart"/>
- </#if>
- <#if book.month == 2>
- <w:vMerge/>
- </#if>
- </w:tcPr>
首先我们要在指定位置添加一个图片,我们保存为xml文件后,我们的图片信心会变为base64转码的字符串,将这部分进行删除,替换为${images},所以我们进行填充时也要是base64转码后的数据
最后将我们的文件修改为 .ftl 格式的文件复制到我们 templates文件夹下
首先要导入我们的freemarker依赖
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-freemarker</artifactId>
- </dependency>
定义我们要填充的数据
public Map<String,Object> dataMap() throws IOException { //获取基础数据 Map<String , Object> map = new HashMap<>(); map.put("name","qtt"); map.put("age","25"); //获取表格数据 List<Map<String,Object>> bookList = new ArrayList<>(); for (int i = 0; i < 5; i++) { Map<String, Object> map1 = new HashMap<>(); map1.put("month",i); map1.put("income", i); map1.put("expense", i +100); bookList.add(map1); } map.put("bookList",bookList); //获取图片流且进行base64转码 File file = new File("C:\\Users\\Administrator\\Desktop\\teacher\\01.jpg"); FileInputStream fileInputStream = new FileInputStream(file); byte[] bytes = new byte[fileInputStream.available()]; fileInputStream.read(bytes); BASE64Encoder base64Encoder = new BASE64Encoder(); String encode = base64Encoder.encode(bytes); map.put("images",encode); //关闭流 fileInputStream.close(); return map; }
定义我们我们的填充方法
@Test public void insertWord() throws IOException, TemplateException { //定义我们的编码方式 Configuration configuration = new Configuration(); configuration.setDefaultEncoding("UTF-8"); //指定我们word的目录 configuration.setDirectoryForTemplateLoading(new File("src/main/resources/templates")); //指定我们要使用的word模板.ftl Template template = configuration.getTemplate("模板.ftl", "UTF-8"); //指定输出流到的位置 BufferedWriter bufferedWriter = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("E:/222/demo.doc"), "UTF-8")); //执行填充输出 template.process(this.dataMap(),bufferedWriter); //关闭io流 bufferedWriter.flush(); bufferedWriter.close(); }
我们还可以直接定义为下载,不用使用输出流指定下载地址,直接通过下载的方式指定地址
@GetMapping("/upload") public void upload(HttpServletResponse response){ try { //定义我们的编码方式 Configuration configuration = new Configuration(); configuration.setDefaultEncoding("UTF-8"); //指定我们word的目录 configuration.setDirectoryForTemplateLoading(new File("src/main/resources/templates")); //指定我们要使用的word模板.ftl Template template = configuration.getTemplate("模板.ftl", "UTF-8"); //返回word文档 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss"); String format = simpleDateFormat.format(new Date()); String fileName = URLEncoder.encode("接口文档" + format, "UTF-8"); response.setCharacterEncoding("UTF-8"); //请求头定义为下载 response.setHeader("Content-Disposition","attachment;filename="+fileName+".doc"); //获取apiDoc所需要的数据 Map<String, Object> map = dataMap(); //渲染模板 template.process(map, response.getWriter()); //response的Writer不需要我们手动关,tomcat会帮我们关的 } catch (Exception e) { log.error("导出word异常:", e); } }
1.优为注意:freemarker是不支持 null 的,所以我们的数据要么全部附上值,要么给为空的值设置为 " " ,否则会报错
2.我们在定义表格l遍历填充的时候,一定要注意传入的表格字段类型是list,不能是map,否则会报错
3.和并单元格的时候,查看的文章,它定义的标签是m是小写,实现不了合并,看其他文章都为大写M,修改实现了合并,有大小写区分
无法合并的情况
- <w:vmerge w:val="restart"/>
- <w:vmerge/>
修改后可以合并
- <w:vMerge w:val="restart"/>
- <w:vMerge/>
联系客服