之前java总用freemaker进行导出,但是改xml实在是太繁琐了,这次找了另一个工具进行体验.
一、简单导出
pom引入
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.core</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.document</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.template</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.document.docx</artifactId>
<version>2.0.6</version>
</dependency>
<dependency>
<groupId>fr.opensagres.xdocreport</groupId>
<artifactId>fr.opensagres.xdocreport.template.freemarker</artifactId>
<version>2.0.6</version>
</dependency>
导出代码
public void genDoc() {
RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes attributes = (ServletRequestAttributes) requestAttributes;
HttpServletResponse response = attributes.getResponse();
try (InputStream inputStream = POICacheManager.getFile("template.docx")) {
// 3. 读取模板,创建 IXDocReport 对象
IXDocReport report = XDocReportRegistry.getRegistry().loadReport(inputStream, TemplateEngineKind.Freemarker);
// 4. 创建上下文,设置变量(你可以按需扩展)
IContext context = report.createContext();
context.put("name", "张三");
//编辑域代码 ctrl F9 类别选 邮件合并 域名mergefield 域代码后面填 ${name}
// 设置响应头,准备输出 Word 文件
response.setContentType("application/vnd.openxmlformats-officedocument.wordprocessingml.document");
response.setHeader("Content-Disposition", "attachment; filename=generated-document.docx");
// 5. 生成 Word 并写入响应输出流
try (OutputStream out = response.getOutputStream()) {
report.process(context, out);
}
} catch (Exception e) {
log.error(">>> 生成 Word 失败:", e);
// 手动返回错误信息
try {
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
response.getWriter().write("生成文档失败:" + e.getMessage());
} catch (Exception ex) {
log.error(">>> 写回错误信息失败", ex);
}
}
}
在resource目录新建word文件名为template.docx
打开template.docx
输入任意字符,然后 Ctrl+9插入域,然后右键编辑域,类别选 邮件合并 域名选mergefield 域代码后面填 ${name} 保存后用上面的代码导出
导出后可以看到域代码位置被填充为张三了。
二、进阶用法
1.动态表格行
分两种情况处理
(1)第一列为序号列
将第一列填充3个域
"@before-row[#list productInfoList as item]"
${item?index+1}
@after-row[/#list]
第二列 填充实际的字段即可
如
${item.name}
(2)第一列不序号列
将上文中的序号表达式${item?index+1} 换成你想填充的表达式,如
${item.code}
其他处理跟上卖弄一样
在java中的定义,先定义实体,然后context.put即可
//假设之前定义了productInfo实体,实体有code name等字段
List<ProductInfo> productInfoList = new ArrayList();
...
context.put("productInfoList", productInfoList );
2.图片
图片不用域,在要导入的地方插入一张图片,然后建立书签,建立书签的方法如下
选中文档中的某张图片,单击【插入】菜单下【链接】子菜单中的【书签】。
这里的书签名跟上面的域变量名类似
java代码
//根据文件读取
IImageProvider iImageProvider = new FileImageProvider(file, false);
context.put("pic", iImageProvider);
//dataurlbase64的字符串,不含前缀
byte[] imageBytes = Base64.getDecoder().decode(base64Image);
//创建 ByteArrayImageProvider 实例
ByteArrayImageProvider imageProvider = new ByteArrayImageProvider(imageBytes, "png");
context.put("pic", imageProvider );
3.条件显示隐藏
使用if标签,下面是3个域
[#if data.ccUser??] 申请人:${data.ccUser } [/#if]
可以看到两个if标签域包裹了文字和变量
4.空值处理
空值直接使用会报错,可使用表达式
${tx.amount!}
或者
${tx.amount?if_exists}
上面表达式表示有值填充,无值不填充
5.单元格合并
实现起来比较复杂,在模板中先合并,然后对多列使用第一节的动态表格行基本上可以实现按层级合并,如果是跨列合并,比较复杂的合并目前还没找到好的解决方案?或许需要考虑原生freemaker或者poi-tl?
6.关键字
在实体中应尽量避免使用以下关键字
length