前言:项目中通常会需要导出合同,导出周报等需求等。本文是基于poi技术,实现通过填充word模板生成word文件并支持下载。
话不多说,上代码:
public class WordUtils {
private static Logger logger = LoggerFactory.getLogger(WordUtils.class);
/**
* 根据word模板填充数据并下载
*
* @param templateAbsolutePath:模板文件绝对路径
* @param data:写入模板的数据
* @param response
*/
public static void generateWordByTemplate(String templateAbsolutePath, Map<String, Object> data, HttpServletResponse response) {
//
File wordTemplate = new File(templateAbsolutePath);
try {
//创建临时文件
File newFile = File.createTempFile("hwt", ".docx");
//循环插入策略
LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
//策略绑定位置
Configure build = Configure.builder().bind(policy, "contractTable", "returnMoneyTable").build();
//XWPFTemplate读取模板文件并渲染数据
XWPFTemplate render = XWPFTemplate.compile(wordTemplate, build).render(data);
//数据渲染结束后,设置样式
setTableStyle(data,render);
//渲染完成的数据写临时文件
render.writeToFile(newFile.getAbsolutePath());
//文件下载
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("周报.docx", "UTF-8"));
FileInputStream in = new FileInputStream(newFile.getAbsolutePath());
int len = 0;
//创建缓冲区写入文件数据
byte[] buffer = new byte[1024];
ServletOutputStream out = response.getOutputStream();
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
//关闭io
in.close();
out.close();
//删除临时文件
newFile.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
}
上word模板
word模板数据填充是采用的占位符方式,其中数据采用key-value接口,推荐采用HashMap。
模板中部分占位符解释说明:
- 基本上所有的变量都由{{}}包裹,交给poi-tl解析(变量命名随便你,中文,英文都可以)
- {{contractTable}}:下方的[departmentName]、[targetMoney]、[totalMoney]、[salary]、[finishPercent] 就是使用了 poi-tl的表格行插件,动态渲染表格行的语法,可以简单理解为此处表格对应的每一行就是一个对象实体类,而{{contractTable}}就是一个list,则Map中对应的数据为:key:contractTbale, value:list;其中list列表的对象中字段名需要和 [] 里一一对应
- {{?importantProjects}} {{item}} {{/importantProjects}},此处则是使用了poi-tl的区块对标签。区块对在此处的,我只是用它来实现渲染同一个表格内的多行文本渲染,可以理解为{{?importantProjects}} {{item}} {{/importantProjects}}对应的就是一个list,< item >就是一个list中的一个个对象元素。并且此处会触发,换行后序号累加的word编排编号样式。数据格式为:key:importantPeojects; value:List<Map>,其中list中的map的key为item,需要与区块对标签中占位符对应
- {{@***}},此处需要注意,word模板中代表图片的变量,必须要加上@特殊符号修饰!
来,展示!
【补充-扩展】
上面展示的是最基本的word动态渲染过程。最后由于近期需求的调整,需要对word中的动态表格设置样式,查询了很多资料,网上对于word中的动态表格Table样式的修改案例较少,特此记录一下。
/**
* 设置周报word中table的样式
* 注意:该方法和word模板及数据强依赖,没有普适性!!!
* word中表格的单元格样式可通过XWPFRun来设置
* 本段代码只可参考其中的部分类、方法的使用,没有普适性
* @param data
* @param render
*/
private static void setTableStyle(Map<String, Object> data, XWPFTemplate render) {
BigDecimal timePercent = (BigDecimal) data.get("timePercent");
List<XWPFTable> tables = render.getXWPFDocument().getTables();
for (XWPFTable table : tables) {
List<XWPFTableRow> rows = table.getRows();
if (CollectionUtils.isNotEmpty(rows)) {
for (int i = 2; i < rows.size(); i++) {//前两行为吧表头,过滤掉前两行
List<XWPFTableCell> rowCells = rows.get(i).getTableCells();
if (i == 2 || i == 5 || i == 8) {
for (XWPFTableCell cell : rowCells) {
cell.getParagraphArray(0).getRuns().get(0).setBold(true);//单元格字体加粗
if (i == 2 || i == 5) {
cell.getCTTc().getTcPr().addNewTcBorders().addNewBottom().setVal(STBorder.NIL);
}
}
} else if (i == 3 || i == 4 || i == 6 || i == 7) {
for (XWPFTableCell cell : rowCells) {
XWPFRun run = cell.getParagraphArray(0).getRuns().get(0);
run.setItalic(true);//单元格字体倾斜
run.setFontFamily("楷体");//字体格式设置为楷体,默认只对ASCII(0-127)码有效,特殊的比如中文需要指定范围FontCharRange,可参考下面的代码
if (i == 3 || i == 6) {
cell.getCTTc().getTcPr().addNewTcBorders().addNewBottom().setVal(STBorder.NIL);//单元格底部边框隐藏
cell.getCTTc().getTcPr().addNewTcBorders().addNewTop().setVal(STBorder.NIL);//单元格顶部边框隐藏
}
if (i == 4 || i == 7) {
cell.getCTTc().getTcPr().addNewTcBorders().addNewTop().setVal(STBorder.NIL);
}
}
rowCells.get(0).getParagraphArray(0).getRuns().get(0).setFontFamily("楷体", XWPFRun.FontCharRange.eastAsia);//第一列是中文 ,需要使用eastAsia才能生效;但是eastAsia对是ascii(0-127)又无效,所以需要单独设置
} else {
if ((new BigDecimal(rowCells.get(rowCells.size() - 1).getText().split("%")[0])).compareTo(timePercent) < 0) {
rowCells.get(rowCells.size() - 1).getParagraphArray(0).getRuns().get(0).setBold(true);
rowCells.get(rowCells.size() - 1).getParagraphArray(0).getRuns().get(0).setColor("FF0000");//FF0000是红色的rgb16进制数值,rgb16进制数值可以通过https://www.rgbku.com/zhuanhuanqi.html计算
}
}
}
}
}
}