Java使用XDocReport和FreeMarker模板引擎导出Word文档
本篇介绍配合前端控制输入,调用后端接口导出Word的方法,后端通过 XDocReport 和 FreeMarker 模板引擎生成Word文档后直接返回文件流给前端下载。
示例
前端页面
word导出样例
前端
前端使用vue、element-ui编写页面,此篇不做赘述。
后端
1、准备模版
将word中需要动态填充的字段变为word域,word域是引导Word在文档中自动插入文字、图形、页码或其他信息的一组代码。在这里表示Word文档中要被替换的内容。
编辑普通word模版文本步骤:
1、在word模板中选中要替换的文本,拿姓名为例,光标放置在姓名表格后,点击插入“域”:
2、在弹出的对话框中,类别选择“邮件合并”,在后侧域代码的 "MERGEFIELD "后面编写FreeMarker模板表达式${name!},点击【确定】按钮:
注:name后面的!是为防止空值导致报错,若有可能为空值的最好都加上。
3、编辑好后的效果如下图:
4、把word模板需要替换的内容都替换成模板变量:
处理word模版中的表格数据
表格数据的处理其实和上面对普通文本的处理是类似的,只不过要在Word模板中加上集合的变量。
具体步骤如下:
1、选定表格中要替换的文本,光标放置在此后,点击插入“域”;
2、在弹出的对话框中,类别选择“邮件合并”,在后侧域代码的 "MERGEFIELD "后面编写FreeMarker模板表达式${eduBack.startingEndingDay!},点击【确定】按钮:
3、重复步骤2,替换表格中的其他需要替换的地方:
注: Word模板中的表格的长度最好充满Word文档的左右两边,否则如果表格下面还有其他文本内容,下面的文本内容会自动填充到表格的缝隙处,而且会对下面的文本内容进行覆盖。
2、将准备好的模版放到项目中
放在java项目的resources目录下:
3、代码
添加maven依赖到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 wordExport;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import fr.opensagres.xdocreport.core.XDocReportException;
import fr.opensagres.xdocreport.document.IXDocReport;
import fr.opensagres.xdocreport.document.images.FileImageProvider;
import fr.opensagres.xdocreport.document.images.IImageProvider;
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 javax.servlet.http.HttpServletResponse;
import XXXX.entity.*;
@Service
public class Test {
//实现类
public void export(Map<String, Object> params,HttpServletResponse response) throws Exception {
//获取Word模板
InputStream ins = this.getClass().getResourceAsStream("/model1.docx");
//注册xdocreport实例并加载FreeMarker模板引擎
IXDocReport report = XDocReportRegistry.getRegistry().loadReport(ins,TemplateEngineKind.Freemarker);
//设置集合数组
List<EduBack> listEduBack = new ArrayList<EduBack>();
List<Map<String, String>> paramEduBackList = (List<Map<String, String>>) params.get("eduBackTableData");
for (Map<String, String> eduBack : paramEduBackList) {
EduBack eduBackMap = new EduBack();
eduBackMap.setStartingEndingDay(eduBack.get("startingEndingDay"));
eduBackMap.setGraduateSchool(eduBack.get("graduateSchool"));
eduBackMap.setEducationalBackground(eduBack.get("educationalBackground"));
eduBackMap.setProfession(eduBack.get("profession"));
eduBackMap.setLearningForm(eduBack.get("learningForm"));
listEduBack.add(eduBackMap);
}
//创建xdocreport上下文对象
IContext context = report.createContext();
context.put("name",params.get("name"));
context.put("sex",params.get("sex"));
context.put("birthDay",params.get("birthDay"));
context.put("eduBack",listEduBack);//加入到上下文对象中
//创建字段元数据
FieldsMetadata fm = report.createFieldsMetadata();
//Word模板中的表格数据对应的集合类型
fm.load("eduBack", EduBack.class, true);
report.setFieldsMetadata(fm);
//输出到本地目录
String fileName = params.get("name")+"模版.docx";
//FileOutputStream out = new FileOutputStream(new File("D://"+fileName));
//report.process(context, out);
//浏览器端下载
response.setCharacterEncoding("utf-8");
response.setContentType("application/*");
response.setHeader("Content-Disposition", "attachment;filename=".concat(String.valueOf(URLEncoder.encode(fileName, "UTF-8"))));
report.process(context, response.getOutputStream());
}
}
4、补充-生成带有图片的word
在之前准备好的word模版中要加入图片的位置插入一张模版图片,然后选中模版图片插入一个书签,设置书签名,点击添加按钮。
若需要添加多张图片,重复此操作即可。
java代码
//创建字段元数据
FieldsMetadata fm = report.createFieldsMetadata();
//Word模板中的表格数据对应的集合类型
fm.load("eduBack", EduBack.class, true);
fm.addFieldAsImage("img1");
context.put("img1",params.get("imgInputStream"));//放入前端传入的图片流
report.setFieldsMetadata(fm);
导出效果如下