1.技术poi-tl
项目开发中经常会根据数据生成对应的word模板
代码案例:https://gitee.com/J-summit/note-sty-blogs/tree/master/src/main/java/tech/cn/note/word
2.代码实现
引入依赖
implementation group: 'com.deepoove', name: 'poi-tl', version: '1.10.0'
package tech.cn.note.word;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.Map;
import javax.annotation.PostConstruct;
import cn.hutool.json.JSONUtil;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import com.deepoove.poi.plugin.table.LoopRowTableRenderPolicy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import tech.cn.note.utils.CheckUtil;
import tech.cn.note.word.fun.RenderFunction;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
@Slf4j
@RequiredArgsConstructor
public class PoiTemplateDataFillServiceImpl {
private static Configure configure;
private static final ConfigureBuilder builder = Configure.builder();
private final Map<String, RenderFunction> strategyMap;
@Value("${custom.nullError:false}")
private Boolean nullError;
public static final ThreadLocal threadLocal = new ThreadLocal();
@PostConstruct
private void init() {
builder.buildGramer("${", "}");
builder.useSpringEL(false);
builder.addPlugin('~', new LoopRowTableRenderPolicy());
builder.addPlugin('%', new StrongRenderPolicy(strategyMap));
builder.addPlugin('\u0000', new CustomTextRenderPolicy());
configure = builder.build();
}
public byte[] writeToByte(Map<String, Object> valueMap, byte[] templateFileBytes) throws Exception {
try {
CheckUtil.checkNotNull(templateFileBytes, "模板文件不能为空");
CheckUtil.checkNotNull(valueMap, "数据源为空");
XWPFTemplate template = prepare(new ByteArrayInputStream(templateFileBytes), valueMap);
//如果需要输出PDF则进行转换
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
template.write(byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
} finally {
threadLocal.remove();
}
}
/**
* poi-tl框架填充数据
*
* @param inputStream 模板字节文件流
* @param map 数据源
* @return 填充结束的XWPFTemplate
*/
private XWPFTemplate prepare(ByteArrayInputStream inputStream, Map<String, Object> map) {
if (nullError) {
builder.setValidErrorHandler(new Configure.AbortHandler());
} else {
builder.setValidErrorHandler(new Configure.ClearHandler());
}
configure = builder.build();
log.debug("data is {}", JSONUtil.parse(map));
threadLocal.set(map);
return XWPFTemplate.compile(inputStream, configure).render(map);
}
}
2.复杂运算符
官方文档 https://deepoove.com/poi-tl/
3.表格循环
4.自定义函数
案例实现数字中文大写
重写渲染实现逻辑
比如我们用正则匹配到 %methodName
String functionStr;
// 现在创建 matcher 对象
Matcher matcher = PATTERN.matcher(placeHolder);
if (matcher.find()) {
functionStr = matcher.group(1);
} else {
throw new RuntimeException("[" + placeHolder + "]表达式不符合规则,请按照${%function(var,var2,var3)}格式");
}
RenderFunction renderFunction = functionMap.get(functionStr);
if (renderFunction == null) {
throw new RuntimeException(String.format("不支持的函数%s", functionStr));
}
String vars = matcher.group(2);
CheckUtil.checkExpression(StringUtils.isNotEmpty(vars), "表达式不符合规则,请按照${%function(var,var2,var3)}格式");
String result = renderFunction.doCalculate(vars.split(","), renderDataCompute);
if (result == null || StrUtil.NULL.equals(result)) {
run.setText("", 0);
return;
}
package tech.cn.note.word.fun;
import cn.hutool.core.convert.NumberChineseFormatter;
import com.deepoove.poi.render.compute.RenderDataCompute;
import org.springframework.stereotype.Service;
import static org.springframework.util.ObjectUtils.isEmpty;
@Service
public class ChineseMoney implements RenderFunction {
/**
* @param fields 参数1 数字
* @param renderDataCompute
* @return
*/
@Override
public String doCalculate(String[] fields, RenderDataCompute renderDataCompute) {
if (isEmpty(fields)) {
return "";
}
String placeHolder = fields[0];
Object data = renderDataCompute.compute(placeHolder);
if (data == null) {
return "";
}
return NumberChineseFormatter.format(Double.parseDouble(data.toString()), true, true);
}
}