最近弄一个业务需要搞很多的word文档导出,供前端下载。之前的实现方式一般是先把word转成XML格式,然后赋值变量,这种方式虽然可行,但是遇到那种长篇且变量又多的文档,就很让人头大,密密麻麻的一堆代码,看着十分繁乱,不好修改模板。于是在技术选型上找到了XDocReport,该技术可以直接使用word文档作为模板,使用Velocity或Freemarker语法来设置要替换的变量,从而实现复杂的文档导出。
一.XDocReport技术Java实现
使用该模板的操作主要是 IXDocReport 和 IContext 对象,封装两个工具类来对他们进行获取和操作,用 IContext 的 put(key,value)赋值模板变量即可
java
//获取Word模板,模板存放路径在项目的resources目录下template文件夹
InputStream ins = this.getClass().getClassLoader().getResourceAsStream("template" +
File.separator + "test.docx");
IXDocReport report = XDocReportRegistry.getRegistry().loadReport(ins,
TemplateEngineKind.Freemarker);
//创建xdocreport上下文对象
IContext context = report.createContext();
//创建字段元数据
FieldsMetadata fm = report.createFieldsMetadata();
//1.业务逻辑
//2.模板传递参数
//2.1集合变量
context.put("certList", certificateList);
fm.load("certList", CbgkShipCertificate.class, true);
//2.2实体变量
context.put("cancelCertify", cancelCertifyVO);
fm.load("cancelCertify", CbgkCancelCertifyVO.class, false);
//2.3单一变量
context.put("moreCatalog", 0);
//2.4图片变量
fm.addFieldAsImage("imgUrl");
File file = UrltoFile(personImage.getUrl()); //该方法在下头
IImageProvider iImageProvider = new FileImageProvider(file, false);
context.put("imgUrl", iImageProvider);
report.setFieldsMetadata(fm);
response.setContentType("application/msword");
response.setCharacterEncoding("UTF-8");
String fileName = URLEncoder.encode(peopleInfoVO.getName() + "综合材料.docx", "UTF-8");
response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
try (ServletOutputStream outputStream = response.getOutputStream()) {
report.process(context, outputStream);
outputStream.flush();
}
java
/**
* 将Url转换为File
* @param url
* @return
* @throws Exception
*/
public static File UrltoFile(String url) throws Exception {
log.error("图片的url: " + url);
InputStream ins = null;
File file = null;
OutputStream os = null;
HttpURLConnection httpUrl = null;
try {
httpUrl = (HttpURLConnection) new URL(url).openConnection();
httpUrl.setConnectTimeout(300000);
httpUrl.setReadTimeout(300000);
httpUrl.setRequestProperty("Content-Length", "1000");
httpUrl.connect();
ins = httpUrl.getInputStream();
file = new File(System.getProperty("java.io.tmpdir") + File.separator + "seat" + System.currentTimeMillis());
if (file.exists()) {
file.delete();
}
os = new FileOutputStream(file);
int bytesRead;
int len = 8192;
byte[] buffer = new byte[len];
while ((bytesRead = ins.read(buffer, 0, len)) != -1) {
os.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
log.error("地址请求不可用或响应超时");
e.printStackTrace();
} finally {
os.close();
ins.close();
if (httpUrl != null) {
httpUrl.disconnect();
}
}
return file;
}
二.Word模板的制作
2.1 新建word文档,在需要替换变量位置的地方键盘Ctrl + F9,鼠标右击编辑域,选择邮件合并类型,输入"MERGEFIELD ${name}"
2.2 插入模板图片
插入图片书签,调整尺寸大小,方便变量替换
模板制作的基操基本如此,就不详细介绍了,这边的变量赋值的控制语言作者是使用的Freemarker,如需要复杂实现的可以百度了解下这个模板引擎的语句。这里作者提供一些常见的语句参考。
bash
//1.判断catalogFirstList集合非空则遍历该集合,单实体使用别名catalog
MERGEFIELD @before-row[#if (catalogFirstList)??][#list catalogFirstList as catalog]
MERGEFIELD ${catalog.name!}
MERGEFIELD @after-row[/#list][/#if]
//2.if语句
MERGEFIELD [#if (notarize.purpose)??][#if notarize.purpose=='其它船舶']☑[#else]□[/#if][#else]□[/#if]
MERGEFIELD [#if shipownerType == '0']
MERGEFIELD [/#if]
//3.大于等于
MERGEFIELD [#if shipPicSize gte 1]
//4.大于
MERGEFIELD [#if shipPicSize > 0]
5.或运算
MERGEFIELD [#if shipowner.type == '1' || shipowner.type == '0']
6.时间格式化(防止非空报错)
MERGEFIELD [#if (record.takeOverTime)??]
MERGEFIELD ${record.takeOverTime?string('yyyy-MM-dd HH:mm:ss')}
MERGEFIELD [/#if]
7.单个变量防止空指针
MERGEFIELD ${notarize.name!}