前言

在日常的开发工作中,我们时常会遇到导出Word文档报表的需求,比如公司的财务报表、医院的患者统计报表、电商平台的销售报表等等。

导出Word方式多种多样,通常有以下几种方式:

1. 使用第三方Java工具类库Hutool的Word工具类,参考网址为https://www.hutool.cn/docs/#/poi/Word生成-Word07Writer;

2. 利用Apache POI和FreeMarker模板引擎;

3. 第三方报表工具。

上面的几种方式虽然可以实现Word文档的导出,但有以下缺点

第一种方式操作简单,但也只能生成简单的Word文档,无法生成有表格的Word文档;

第二种方式可以生成复杂的Word文档,但是还要进行Word转xml,xml转ftl的双重转换,不适合内容经常变更的Word文档;

第三种方式有时候不适合对格式要求严格的文档。

那么,有没有既简单又高效的导出Word的方法呢?答案是肯定有的。接下来我就来介绍一种用Java语言实现的,通过XDocReportFreeMarker模板引擎生成Word文档的方法。

准备环境

开发语言:

Java7及以上的版本。

开发工具:

Eclipse/Idea。

第三方依赖库:

XDocReport、POI、Freemarker。

模板语言:

FreeMarker。

Word编辑器:

Microsoft 365或其他版本较高的Word编辑器。

示例Word模板

制作模板

Word模板如上图,可以看到,结构比较简单,包括两个部分,第一部分是纯文字和数字,第二部分主要是表格。我们在实际的开发过程中生成的报表几乎都是动态生成的,所以模板中的数字和表格里的数据都要替换成我们后台的实际数据。

替换Word模板中的动态变量,我们需要掌握两个知识点:

1.Word文档中的Word域,word域是引导Word在文档中自动插入文字、图形、页码或其他信息的一组代码。在这里我们可以把         Word域理解成标识符,这个标识符表示Word文档中要被替换的内容;

2.FreeMarker模板下的变量表达式,比如用${city}替换Word示例模板中的北京市。

了解了以上两个概念后,我们就可以动手编辑Word模板了,步骤如下:

1. 首先在Word模板中选中要替换的文本,在这儿拿标题中的"北京市"为例,然后键盘使用 Ctrl + F9 组合键将其设置为域,此时文本会被"{}"包围,接着鼠标右键选择【编辑域(E)...】:

2. 在弹出的对话框中,类别选择“邮件合并”,域名选择 "MergeField",域属性中的域名填入模版表达式${city},点击【确定】按钮:

3. 编辑后的效果如下:

4. 掌握替换文本的方法后,我们可以把Word模板第一部分需要替换的内容都替换成模板变量:

Word模板中表格数据的处理

表格中的数据实质上就是对集合的遍历。

表格数据的处理其实和上面对文本内容的处理是类似的,只不过要在Word模板中加上集合的变量,Java代码中也要有对集合进行特对的处理(这个在后面的代码展示部分会说)。

具体操作步骤如下:

1. 选定表格中要替换的文本,然后键盘使用 Ctrl + F9 组合键将其设置为域,接着鼠标右键选择【编辑域(E)...】:

2. 在弹出的对话框中,类别选择“邮件合并”,域名选择 "MergeField",域属性中的域名填入模版表达式${goods.num},点击【确定】按钮;

3. 重复步骤2,替换表格中的其他文本内容:

后台代码

添加依赖到pom.xml文件

  1. <dependency>
  2. <groupId>org.apache.poi</groupId>
  3. <artifactId>poi</artifactId>
  4. <version>4.1.1</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.apache.poi</groupId>
  8. <artifactId>poi-ooxml</artifactId>
  9. <version>4.1.1</version>
  10. </dependency>
  11. <dependency>
  12. <groupId>org.jxls</groupId>
  13. <artifactId>jxls</artifactId>
  14. <version>2.6.0</version>
  15. <exclusions>
  16. <exclusion>
  17. <groupId>ch.qos.logback</groupId>
  18. <artifactId>logback-core</artifactId>
  19. </exclusion>
  20. </exclusions>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.jxls</groupId>
  24. <artifactId>jxls-poi</artifactId>
  25. <version>1.2.0</version>
  26. </dependency>
  27. <dependency>
  28. <groupId>fr.opensagres.xdocreport</groupId>
  29. <artifactId>fr.opensagres.xdocreport.core</artifactId>
  30. <version>2.0.2</version>
  31. </dependency>
  32. <dependency>
  33. <groupId>fr.opensagres.xdocreport</groupId>
  34. <artifactId>fr.opensagres.xdocreport.document</artifactId>
  35. <version>2.0.2</version>
  36. </dependency>
  37. <dependency>
  38. <groupId>fr.opensagres.xdocreport</groupId>
  39. <artifactId>fr.opensagres.xdocreport.template</artifactId>
  40. <version>2.0.2</version>
  41. </dependency>
  42. <dependency>
  43. <groupId>fr.opensagres.xdocreport</groupId>
  44. <artifactId>fr.opensagres.xdocreport.document.docx</artifactId>
  45. <version>2.0.2</version>
  46. </dependency>
  47. <dependency>
  48. <groupId>fr.opensagres.xdocreport</groupId>
  49. <artifactId>fr.opensagres.xdocreport.template.freemarker</artifactId>
  50. <version>2.0.2</version>
  51. </dependency>
  52. <dependency>
  53. <groupId>org.freemarker</groupId>
  54. <artifactId>freemarker</artifactId>
  55. <version>2.3.23</version>
  56. </dependency>
  57. <dependency>
  58. <groupId>commons-io</groupId>
  59. <artifactId>commons-io</artifactId>
  60. <version>2.5</version>
  61. </dependency>

编写Java代码

  1. package com.tzsj.test;
  2. import java.io.File;
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import java.util.ArrayList;
  7. import java.util.List;
  8. import org.junit.Test;
  9. import org.springframework.stereotype.Controller;
  10. import org.springframework.web.bind.annotation.RequestMapping;
  11. import fr.opensagres.xdocreport.core.XDocReportException;
  12. import fr.opensagres.xdocreport.document.IXDocReport;
  13. import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
  14. import fr.opensagres.xdocreport.template.IContext;
  15. import fr.opensagres.xdocreport.template.TemplateEngineKind;
  16. import fr.opensagres.xdocreport.template.formatter.FieldsMetadata;
  17. import io.renren.entity.Goods;
  18. @Controller
  19. @RequestMapping("/word")
  20. public class WordTest {
  21. @Test
  22. public void test() throws IOException, XDocReportException {
  23. generateWord();
  24. }
  25. public void generateWord() throws IOException, XDocReportException {
  26. //获取Word模板,模板存放路径在项目的resources目录下
  27. InputStream ins = this.getClass().getResourceAsStream("/模板.docx");
  28. //注册xdocreport实例并加载FreeMarker模板引擎
  29. IXDocReport report = XDocReportRegistry.getRegistry().loadReport(ins,
  30. TemplateEngineKind.Freemarker);
  31. //创建xdocreport上下文对象
  32. IContext context = report.createContext();
  33. //创建要替换的文本变量
  34. context.put("city", "北京市");
  35. context.put("startDate", "2020-09-17");
  36. context.put("endDate", "2020-10-16");
  37. context.put("totCnt", 3638763);
  38. context.put("totAmt", "6521");
  39. context.put("onCnt", 2874036);
  40. context.put("onAmt", "4768");
  41. context.put("offCnt", 764727);
  42. context.put("offAmt", "1753");
  43. context.put("typeCnt", 36);
  44. List<Goods> goodsList = new ArrayList<Goods>();
  45. Goods goods1 = new Goods();
  46. goods1.setNum(1);
  47. goods1.setType("臭美毁肤");
  48. goods1.setSv(675512);
  49. goods1.setSa("589");
  50. Goods goods2 = new Goods();
  51. goods2.setNum(2);
  52. goods2.setType("女装");
  53. goods2.setSv(602145);
  54. goods2.setSa("651");
  55. Goods goods3 = new Goods();
  56. goods3.setNum(3);
  57. goods3.setType("手机");
  58. goods3.setSv(587737);
  59. goods3.setSa("866");
  60. Goods goods4 = new Goods();
  61. goods4.setNum(4);
  62. goods4.setType("家具家装");
  63. goods4.setSv(551193);
  64. goods4.setSa("783");
  65. Goods goods5 = new Goods();
  66. goods5.setNum(5);
  67. goods5.setType("食物饮品");
  68. goods5.setSv(528604);
  69. goods5.setSa("405");
  70. goodsList.add(goods1);
  71. goodsList.add(goods2);
  72. goodsList.add(goods3);
  73. goodsList.add(goods4);
  74. goodsList.add(goods5);
  75. context.put("goods", goodsList);
  76. //创建字段元数据
  77. FieldsMetadata fm = report.createFieldsMetadata();
  78. //Word模板中的表格数据对应的集合类型
  79. fm.load("goods", Goods.class, true);
  80. //输出到本地目录
  81. FileOutputStream out = new FileOutputStream(new File("D://商品销售报表.docx"));
  82. report.process(context, out);
  83. }
  84. }

Word模板中生成序号

给表格数据添加序号是通过后台代码生成的,比如上面的"goods1.setNum(1)"这段代码,其实也可以在Word模板中设置对应的域变量来实现序号的填充。

语法如下:

  1. @before-row[#list sequence as item]
  2. item?index
  3. @after-row[/#list]

在表格中添加上面的表达式,XDocReport就会自动解析并生成序号,表格中的其他字段也需要进行相应的改动:

提示:

1. 序号的表达式要拆成三个域,如下图,要把这三部分分别设置成域;

     设置完成的结果参考上面表格中的序号表达式,表达式中"item?index+1"是因为序号是从0开始的,所以要加1

2. 表格中除序号的列需要改成item.xxx而不是之前的goods.xxx:

3. 生成效果如下:

建议:序号最好在后台生成,用序号表达式生成的序号列会占用比较大的空间,对资源有所浪费。

补充

1. JavaWeb项目中通常是通过浏览器下载的方式来下载Word文档,此时只需要把之前下载到本地的代码改成浏览器端下载的代码即可:

  1. //输出到本地目录
  2. //FileOutputStream out = new FileOutputStream(new File("D://商品销售报表.docx"));
  3. //report.process(context, out);
  4. //浏览器端下载
  5. response.setCharacterEncoding("utf-8");
  6. response.setContentType("application/msword");
  7. String fileName = "商品销售报表.docx";
  8. response.setHeader("Content-Disposition", "attachment;filename="
  9. .concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));
  10. report.process(context, response.getOutputStream());

2. Word模板中的表格的长度最好充满Word文档的左右两边,否则如果表格下面还有其他文本内容,下面的文本内容会自动填充到表格的缝隙处,而且会对下面的文本内容进行覆盖。

加餐

其实,导出Word模板,上面的模板和代码已经够用了,但也有少数模板需要添加图片和图形(比如饼状图)。

制作图片

图片的生成不使用编辑域,使用模板图片和Word的书签功能,而且需要在元数据中加入图片类型的代码,以下为具体步骤:

1. 在Word模版中需要插入图片的位置插入一张模版图片,然后鼠标单击模板图片插入一个书签,设置书签名称,比如img1, 最后点击【添加】按钮:

2. 如果需要插入多个图片,就在需要插入图片的位置插入多个模板图片并插入书签设置对应的书签名称即可,后台代码如下:

  1. package com.tzsj.test;
  2. import java.io.File;
  3. import java.io.FileOutputStream;
  4. import java.io.IOException;
  5. import java.io.InputStream;
  6. import org.junit.Test;
  7. import fr.opensagres.xdocreport.core.XDocReportException;
  8. import fr.opensagres.xdocreport.document.IXDocReport;
  9. import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
  10. import fr.opensagres.xdocreport.template.IContext;
  11. import fr.opensagres.xdocreport.template.TemplateEngineKind;
  12. import fr.opensagres.xdocreport.template.formatter.FieldsMetadata;
  13. public class ImgTest {
  14. @Test
  15. public void test() throws IOException, XDocReportException {
  16. generateWordForImg();
  17. }
  18. public void generateWordForImg() throws IOException, XDocReportException {
  19. //获取Word模板,模板存放路径在项目的resources目录下
  20. InputStream ins = this.getClass().getResourceAsStream("/图片.docx");
  21. //注册xdocreport实例并加载FreeMarker模板引擎
  22. IXDocReport report = XDocReportRegistry.getRegistry().loadReport(ins,
  23. TemplateEngineKind.Freemarker);
  24. //创建xdocreport上下文对象
  25. IContext context = report.createContext();
  26. FieldsMetadata fm = report.createFieldsMetadata();
  27. //元数据中加入图片
  28. fm.addFieldAsImage("img1");
  29. fm.addFieldAsImage("img2");
  30. //获取图片
  31. InputStream img1 = this.getClass().getResourceAsStream("/11.jpg");
  32. InputStream img2 = this.getClass().getResourceAsStream("/33.jpg");
  33. //把图片添加到上下文对象
  34. context.put("img1", img1);
  35. context.put("img2", img2);
  36. //输出到本地目录
  37. FileOutputStream out = new FileOutputStream(new File("D://图片报表.docx"));
  38. report.process(context, out);
  39. }
  40. }

3. 导出效果如下:

制作图形

要在Word文档中生成柱状图、饼状图等图形,需要在项目中引入第三方绘图工具,在这里使用xchart来演示在Word中生成饼状图图形。

生成饼状图和生成图片的方法很类似,具体步骤如下:

1. 在Word模版中需要插入图片的位置插入一张模版图片,然后鼠标单击模板图片插入一个书签,设置书签名称,比如chart,最后点击【添加】按钮:

2. 编写代码:

2.1 在pom.xml文件中添加xchart的依赖:

  1. <dependency>
  2. <groupId>org.knowm.xchart</groupId>
  3. <artifactId>xchart</artifactId>
  4. <version>3.5.4</version>
  5. </dependency>

2.2 后台代码:

  1. package com.tzsj.test;
  2. import java.io.ByteArrayOutputStream;
  3. import java.io.File;
  4. import java.io.FileOutputStream;
  5. import java.io.IOException;
  6. import java.io.InputStream;
  7. import org.junit.Test;
  8. import org.knowm.xchart.BitmapEncoder;
  9. import org.knowm.xchart.PieChart;
  10. import org.knowm.xchart.PieChartBuilder;
  11. import fr.opensagres.xdocreport.core.XDocReportException;
  12. import fr.opensagres.xdocreport.document.IXDocReport;
  13. import fr.opensagres.xdocreport.document.images.ByteArrayImageProvider;
  14. import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
  15. import fr.opensagres.xdocreport.template.IContext;
  16. import fr.opensagres.xdocreport.template.TemplateEngineKind;
  17. import fr.opensagres.xdocreport.template.formatter.FieldsMetadata;
  18. public class ChartTest {
  19. @Test
  20. public void test() throws IOException, XDocReportException {
  21. generateWordForChart();
  22. }
  23. public void generateWordForChart() throws IOException, XDocReportException {
  24. //获取Word模板,模板存放路径在项目的resources目录下
  25. InputStream ins = this.getClass().getResourceAsStream("/饼图.docx");
  26. //注册xdocreport实例并加载FreeMarker模板引擎
  27. IXDocReport report = XDocReportRegistry.getRegistry().loadReport(ins,
  28. TemplateEngineKind.Freemarker);
  29. //创建xdocreport上下文对象
  30. IContext context = report.createContext();
  31. FieldsMetadata fm = report.createFieldsMetadata();
  32. //元数据中加入图片
  33. fm.addFieldAsImage("chart");
  34. PieChart chart = new PieChartBuilder().width(800).height(620)
  35. .title("销售饼图").build();
  36. //给饼图设置对应的值
  37. chart.addSeries("臭美毁肤", 589);
  38. chart.addSeries("女装", 651);
  39. chart.addSeries("手机", 866);
  40. chart.addSeries("家居家装", 783);
  41. chart.addSeries("食物饮品", 405);
  42. //生成饼图
  43. ByteArrayOutputStream baos = new ByteArrayOutputStream();
  44. BitmapEncoder.saveBitmap(chart, baos, BitmapEncoder.BitmapFormat.JPG);
  45. //把饼图添加到上下文对象
  46. context.put("chart", new ByteArrayImageProvider(baos.toByteArray()));
  47. //输出到本地目录
  48. FileOutputStream out = new FileOutputStream(new File("D://饼图报表.docx"));
  49. report.process(context, out);
  50. }
  51. }

3. 导出效果如下:

总结

这就是用Java语言实现,结合XDocReportFreeMarker模板引擎生成Word文档的方法。希望能给致力于开发的小伙伴带来一丝丝帮助。

如果您在浏览过程中遇到什么问题,请在下方评论区给我留言。

本人入驻的其他平台:

  • 微信公众号:弹指时间 。
  • 网易音乐人弹指时间

java导出word相关推荐

  1. java导出word的几种方式

    目前来看,java导出word大致有6种解决方案: 1:Jacob是Java-COM Bridge的缩写,它在Java与微软的COM组件之间构建一座桥梁.使用Jacob自带的DLL动态链接库,并通过J ...

  2. java导出word纸张方向_java导出word的几种方式

    目前来看,java导出word大致有6种解决方案: 1:Jacob是Java-COM Bridge的缩写,它在Java与微软的COM组件之间构建一座桥梁.使用Jacob自带的DLL动态链接库,并通过J ...

  3. Java导出word 图片重复

    Java导出word 需要的库 <dependency><groupId>org.freemarker</groupId><artifactId>fre ...

  4. 【Java用法】使用Java导出word文档的解决方案(适用于从服务器上下载到本地电脑)

    本文目录 一.Controller 二.Service 接口类 三.ServiceImpl 实现类 四.Content-Type 类型与MIME Type类型对照表 最近在做一个word导出功能,需求 ...

  5. 【Java用法】使用Java导出word文档的解决方案(适用于Windows电脑)

    目录 实现方式一.通过原生的POI 实现方式二.通过Hutool工具包 步骤1.添加pom依赖 步骤2.编写几行代码 步骤3.启动项目,大功告成 实现的效果 最近在做一个word导出功能,需求非常简单 ...

  6. java_导出_word_[转载]java导出word的5种方式

    在网上找了好多天将数据库中信息导出到word中的解决方案,现在将这几天的总结分享一下.总的来说,java导出word大致有5种解决方案: 1:Jacob是Java-COM Bridge的缩写,它在Ja ...

  7. JAVA导出Word文档工具EasyWord

    介绍 基于Apache poi封装,在上层做了模型转换的封装,让使用者更加简单方便 只支持docx的导出,不支持doc 下面废话少说 让我们以最快的方式学会用java导出word文档 组件依赖 依赖 ...

  8. 目前java导出word的6种解决方案

    文章转载自:http://www.cnblogs.com/lcngu/p/5247179.html 最近做的项目,需要将一些信息导出到word中.在网上找了好多解决方案,现在将这几天的总结分享一下. ...

  9. java导出word文件损坏_记录一次POI导出word文件的细节问题

    首先百科一下POI是什么: Apache POI是Apache软件基金会的开放源码函式库,POI提供API给Java程序对Microsoft Office格式档案读和写的功能.通过字面意思,我们大概知 ...

  10. java 导出 word_怎么用java导出word

    展开全部 java导出word代码如下: package com.bank.util; import java.awt.Color; import java.io.FileOutputStream; ...

最新文章

  1. 皮一皮:真正的情侣服...一家人轮流穿...
  2. SAP S/4HANA product search implementation debug in backend
  3. 企业大咖教你解决Kubernetes的挑战
  4. 超棒!纯Rust编写的机器学习框架,速度堪比PyTorch
  5. 解决Amlogic S905或S9xxx在安装Armbian_20.02.0卡logo或无反应的方法
  6. sap供应商主数据表_SAP供应商统驭科目调整操作说明
  7. Servlet和Tomcat底层分析
  8. CUDA: 共享内存与同步
  9. C++ RapidXml快速入门
  10. 计算机服务器加载失败,win10系统打开windows Media player听歌提示“服务器运行失败”的详细步骤...
  11. 第一行代码第二版6.4.1小节创建数据库中遇到adb shell出错的问题
  12. 【玩转微信公众平台之二】 账号注册
  13. 60mph和kmh换算_mph换算kmh(mph换算器)
  14. android广告拦截原理,WebView 广告拦截浅析
  15. 深度学习仍然存在的局限性
  16. 计算机考研380分难吗,考研380分相当于高考多少分的难度
  17. 闲置台式机+文件服务器,卖不出闲置台式机再利用之我的nas!
  18. 流式布局FlowLayout使用
  19. konva系列教程4:图形属性
  20. latex 封面右上角出现数字

热门文章

  1. 大疆3508、2006的输出轴的角度获取
  2. 百度智能云实现文字转语音
  3. nullcnv() mysql_sql rpad
  4. java 跟软帝-攀哥学做编程 飞机大战4
  5. sql server 2017 jdbc使用
  6. 安卓 flutter app证书绑定校验抓包绕过
  7. IGBT驱动及保护电路(之一)
  8. 利用稳压二极管构造简单电压控制开关电路
  9. FPGA中用verilog直接读写操作SDRAM
  10. 会声会影2023最新免费版零基础上手视频剪辑工具