1.技術(shù)poi-tl
項目開發(fā)中經(jīng)常會根據(jù)數(shù)據(jù)生成對應(yīng)的word模板
代碼案例:https://gitee.com/J-summit/note-sty-blogs/tree/master/src/main/java/tech/cn/note/word
image.png
image.png
2.代碼實現(xiàn)
引入依賴
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, "數(shù)據(jù)源為空");
XWPFTemplate template = prepare(new ByteArrayInputStream(templateFileBytes), valueMap);
//如果需要輸出PDF則進(jìn)行轉(zhuǎn)換
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
template.write(byteArrayOutputStream);
return byteArrayOutputStream.toByteArray();
} finally {
threadLocal.remove();
}
}
/**
* poi-tl框架填充數(shù)據(jù)
*
* @param inputStream 模板字節(jié)文件流
* @param map 數(shù)據(jù)源
* @return 填充結(jié)束的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.復(fù)雜運算符
官方文檔 https://deepoove.com/poi-tl/
image.png
3.表格循環(huán)
image.png
image.png
4.自定義函數(shù)
image.png
案例實現(xiàn)數(shù)字中文大寫
重寫渲染實現(xiàn)邏輯
比如我們用正則匹配到 %methodName
String functionStr;
// 現(xiàn)在創(chuàng)建 matcher 對象
Matcher matcher = PATTERN.matcher(placeHolder);
if (matcher.find()) {
functionStr = matcher.group(1);
} else {
throw new RuntimeException("[" + placeHolder + "]表達(dá)式不符合規(guī)則,請按照${%function(var,var2,var3)}格式");
}
RenderFunction renderFunction = functionMap.get(functionStr);
if (renderFunction == null) {
throw new RuntimeException(String.format("不支持的函數(shù)%s", functionStr));
}
String vars = matcher.group(2);
CheckUtil.checkExpression(StringUtils.isNotEmpty(vars), "表達(dá)式不符合規(guī)則,請按照${%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 參數(shù)1 數(shù)字
* @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);
}
}