在日常的开发工作中,我们时常会遇到导出Word文档报表的需求,比如公司的财务报表、医院的患者统计报表、电商平台的销售报表等等。
导出Word方式多种多样,通常有以下几种方式:
上面的几种方式虽然可以实现Word文档的导出,但有以下缺点:
第一种方式操作简单,但也只能生成简单的Word文档,无法生成有表格的Word文档;
第二种方式可以生成复杂的Word文档,但是还要进行Word转xml,xml转ftl的双重转换,不适合内容经常变更的Word文档;
第三种方式有时候不适合对格式要求严格的文档。
那么,有没有既简单又高效的导出Word的方法呢?答案是肯定有的。接下来我就来介绍一种用Java语言实现的,通过XDocReport和FreeMarker模板引擎生成Word文档的方法。
第三方依赖库:
XDocReport、POI、Freemarker。
模板语言:
FreeMarker。
Word编辑器:
Microsoft 365或其他版本较高的Word编辑器。
Word模板如上图,可以看到,结构比较简单,包括两个部分,第一部分是纯文字和数字,第二部分主要是表格。我们在实际的开发过程中生成的报表几乎都是动态生成的,所以模板中的数字和表格里的数据都要替换成我们后台的实际数据。
替换Word模板中的动态变量,我们需要掌握两个知识点:
1.Word文档中的Word域,word域是引导Word在文档中自动插入文字、图形、页码或其他信息的一组代码。在这里我们可以把 Word域理解成标识符,这个标识符表示Word文档中要被替换的内容;
2.FreeMarker模板下的变量表达式,比如用${city}替换Word示例模板中的北京市。
了解了以上两个概念后,我们就可以动手编辑Word模板了,步骤如下:
表格中的数据实质上就是对集合的遍历。
表格数据的处理其实和上面对文本内容的处理是类似的,只不过要在Word模板中加上集合的变量,Java代码中也要有对集合进行特对的处理(这个在后面的代码展示部分会说)。
具体操作步骤如下:
添加依赖到pom.xml文件
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>4.1.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>4.1.1</version>
</dependency>
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls</artifactId>
<version>2.6.0</version>
<exclusions>
<exclusion>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.jxls</groupId>
<artifactId>jxls-poi</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.core</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.document</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.template</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.document.docx</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.template.freemarker</artifactId>
<version>2.0.2</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.23</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.5</version>
</dependency>
编写Java代码
package com.tzsj.test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import fr.opensagres.xdocreport.core.XDocReportException;
import fr.opensagres.xdocreport.document.IXDocReport;
import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
import fr.opensagres.xdocreport.template.IContext;
import fr.opensagres.xdocreport.template.TemplateEngineKind;
import fr.opensagres.xdocreport.template.formatter.FieldsMetadata;
import io.renren.entity.Goods;
@Controller
@RequestMapping("/word")
public class WordTest {
@Test
public void test() throws IOException, XDocReportException {
generateWord();
}
public void generateWord() throws IOException, XDocReportException {
//获取Word模板,模板存放路径在项目的resources目录下
InputStream ins = this.getClass().getResourceAsStream("/模板.docx");
//注册xdocreport实例并加载FreeMarker模板引擎
IXDocReport report = XDocReportRegistry.getRegistry().loadReport(ins,
TemplateEngineKind.Freemarker);
//创建xdocreport上下文对象
IContext context = report.createContext();
//创建要替换的文本变量
context.put("city", "北京市");
context.put("startDate", "2020-09-17");
context.put("endDate", "2020-10-16");
context.put("totCnt", 3638763);
context.put("totAmt", "6521");
context.put("onCnt", 2874036);
context.put("onAmt", "4768");
context.put("offCnt", 764727);
context.put("offAmt", "1753");
context.put("typeCnt", 36);
List<Goods> goodsList = new ArrayList<Goods>();
Goods goods1 = new Goods();
goods1.setNum(1);
goods1.setType("臭美毁肤");
goods1.setSv(675512);
goods1.setSa("589");
Goods goods2 = new Goods();
goods2.setNum(2);
goods2.setType("女装");
goods2.setSv(602145);
goods2.setSa("651");
Goods goods3 = new Goods();
goods3.setNum(3);
goods3.setType("手机");
goods3.setSv(587737);
goods3.setSa("866");
Goods goods4 = new Goods();
goods4.setNum(4);
goods4.setType("家具家装");
goods4.setSv(551193);
goods4.setSa("783");
Goods goods5 = new Goods();
goods5.setNum(5);
goods5.setType("食物饮品");
goods5.setSv(528604);
goods5.setSa("405");
goodsList.add(goods1);
goodsList.add(goods2);
goodsList.add(goods3);
goodsList.add(goods4);
goodsList.add(goods5);
context.put("goods", goodsList);
//创建字段元数据
FieldsMetadata fm = report.createFieldsMetadata();
//Word模板中的表格数据对应的集合类型
fm.load("goods", Goods.class, true);
//输出到本地目录
FileOutputStream out = new FileOutputStream(new File("D://商品销售报表.docx"));
report.process(context, out);
}
}
给表格数据添加序号是通过后台代码生成的,比如上面的"goods1.setNum(1)"这段代码,其实也可以在Word模板中设置对应的域变量来实现序号的填充。
语法如下:
@before-row[#list sequence as item]
item?index
@after-row[/#list]
在表格中添加上面的表达式,XDocReport就会自动解析并生成序号,表格中的其他字段也需要进行相应的改动:
提示:
设置完成的结果参考上面表格中的序号表达式,表达式中"item?index+1"是因为序号是从0开始的,所以要加1;
建议:序号最好在后台生成,用序号表达式生成的序号列会占用比较大的空间,对资源有所浪费。
//输出到本地目录
//FileOutputStream out = new FileOutputStream(new File("D://商品销售报表.docx"));
//report.process(context, out);
//浏览器端下载
response.setCharacterEncoding("utf-8");
response.setContentType("application/msword");
String fileName = "商品销售报表.docx";
response.setHeader("Content-Disposition", "attachment;filename="
.concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));
report.process(context, response.getOutputStream());
其实,导出Word模板,上面的模板和代码已经够用了,但也有少数模板需要添加图片和图形(比如饼状图)。
制作图片
图片的生成不使用编辑域,使用模板图片和Word的书签功能,而且需要在元数据中加入图片类型的代码,以下为具体步骤:
package com.tzsj.test;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.junit.Test;
import fr.opensagres.xdocreport.core.XDocReportException;
import fr.opensagres.xdocreport.document.IXDocReport;
import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
import fr.opensagres.xdocreport.template.IContext;
import fr.opensagres.xdocreport.template.TemplateEngineKind;
import fr.opensagres.xdocreport.template.formatter.FieldsMetadata;
public class ImgTest {
@Test
public void test() throws IOException, XDocReportException {
generateWordForImg();
}
public void generateWordForImg() throws IOException, XDocReportException {
//获取Word模板,模板存放路径在项目的resources目录下
InputStream ins = this.getClass().getResourceAsStream("/图片.docx");
//注册xdocreport实例并加载FreeMarker模板引擎
IXDocReport report = XDocReportRegistry.getRegistry().loadReport(ins,
TemplateEngineKind.Freemarker);
//创建xdocreport上下文对象
IContext context = report.createContext();
FieldsMetadata fm = report.createFieldsMetadata();
//元数据中加入图片
fm.addFieldAsImage("img1");
fm.addFieldAsImage("img2");
//获取图片
InputStream img1 = this.getClass().getResourceAsStream("/11.jpg");
InputStream img2 = this.getClass().getResourceAsStream("/33.jpg");
//把图片添加到上下文对象
context.put("img1", img1);
context.put("img2", img2);
//输出到本地目录
FileOutputStream out = new FileOutputStream(new File("D://图片报表.docx"));
report.process(context, out);
}
}
制作图形
要在Word文档中生成柱状图、饼状图等图形,需要在项目中引入第三方绘图工具,在这里使用xchart来演示在Word中生成饼状图图形。
生成饼状图和生成图片的方法很类似,具体步骤如下:
2.1 在pom.xml文件中添加xchart的依赖:
<dependency>
<groupId>org.knowm.xchart</groupId>
<artifactId>xchart</artifactId>
<version>3.5.4</version>
</dependency>
2.2 后台代码:
package com.tzsj.test;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import org.junit.Test;
import org.knowm.xchart.BitmapEncoder;
import org.knowm.xchart.PieChart;
import org.knowm.xchart.PieChartBuilder;
import fr.opensagres.xdocreport.core.XDocReportException;
import fr.opensagres.xdocreport.document.IXDocReport;
import fr.opensagres.xdocreport.document.images.ByteArrayImageProvider;
import fr.opensagres.xdocreport.document.registry.XDocReportRegistry;
import fr.opensagres.xdocreport.template.IContext;
import fr.opensagres.xdocreport.template.TemplateEngineKind;
import fr.opensagres.xdocreport.template.formatter.FieldsMetadata;
public class ChartTest {
@Test
public void test() throws IOException, XDocReportException {
generateWordForChart();
}
public void generateWordForChart() throws IOException, XDocReportException {
//获取Word模板,模板存放路径在项目的resources目录下
InputStream ins = this.getClass().getResourceAsStream("/饼图.docx");
//注册xdocreport实例并加载FreeMarker模板引擎
IXDocReport report = XDocReportRegistry.getRegistry().loadReport(ins,
TemplateEngineKind.Freemarker);
//创建xdocreport上下文对象
IContext context = report.createContext();
FieldsMetadata fm = report.createFieldsMetadata();
//元数据中加入图片
fm.addFieldAsImage("chart");
PieChart chart = new PieChartBuilder().width(800).height(620)
.title("销售饼图").build();
//给饼图设置对应的值
chart.addSeries("臭美毁肤", 589);
chart.addSeries("女装", 651);
chart.addSeries("手机", 866);
chart.addSeries("家居家装", 783);
chart.addSeries("食物饮品", 405);
//生成饼图
ByteArrayOutputStream baos = new ByteArrayOutputStream();
BitmapEncoder.saveBitmap(chart, baos, BitmapEncoder.BitmapFormat.JPG);
//把饼图添加到上下文对象
context.put("chart", new ByteArrayImageProvider(baos.toByteArray()));
//输出到本地目录
FileOutputStream out = new FileOutputStream(new File("D://饼图报表.docx"));
report.process(context, out);
}
}
这就是用Java语言实现,结合XDocReport和FreeMarker模板引擎生成Word文档的方法。希望能给致力于开发的小伙伴带来一丝丝帮助。
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://lebron.blog.csdn.net/article/details/124935696
内容来源于网络,如有侵权,请联系作者删除!