1.目录结构
2.引入依赖
<!--poi-tl word支持-->
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.10.2</version>
</dependency>
3.准备word模版数据
健康宝-同时空协查函模板.docx 内容如下
{ {?section1}}
北京市疾病预防控制中心关于
新型冠状病毒感染病例
接触者的协查函
健康宝注册人员:姓名:{ {healthKitGeneratorName}} ,电话:{ {phone}} ,身份证号:{ {icNumber}} ,二维码见附图,谢谢!
望尽早协助为盼。
疾病预防控制中心
附图:健康宝扫码登记二维码
{ {?qrCode}}
{ {@pictures}}
{ {/qrCode}}
{ {/section1}}
注意点1:如果是普通文字输出在word模版上,则要用{ {变量名}}替代。如果是图片的话。要用{ {@变量名}}替代。
注意点2:遍历模版输出使用的是 区块对 标签,该标签使用格式
{ {?变量名}}
模版和数据
{ {/变量名}}
区块对标签官网地址:Poi-tl Documentation
注意点3:由于项目需求,单独一个协查函可能包含多个二维码图片信息。所以我使用了循环嵌套区块对标签,最外层section1标签是循环输出模版整个协查函模版,与HealthKitLetterInfo类对象对应。内层的qrCode标签是循环输出图片,与HealthKitLetterInfo类内的qrCode属性对应。
4.实体创建
4.1 PointPositionForm类
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class PointPositionForm {
private List<String> healthKitScreenshots;
private String healthKitGeneratorName;
private String phoneNum;
private String idNum;
}
4.2 HealthKitLetterInfo类
由于在paramMap中区块对的标签变量对应的是该类的集合。该实体内的字段属性要与word模版中变量名对应
@Data
@Builder
public class HealthKitLetterInfo {
private String healthKitGeneratorName;
private String phone;
private String icNumber;
private List<Map<String, PictureRenderData>> qrCode;
}
5.word文档输出逻辑
public static void main(String[] args) {
Map<String, Object> paramMap = new HashMap<>();
// 创建两个有数据的实体
List<PointPositionForm> healthKits = new ArrayList<>();
healthKits.add(PointPositionForm.builder()
.phoneNum("1333333333")
.idNum("我是身份证号1")
.healthKitGeneratorName("测试姓名1")
.healthKitScreenshots(Arrays.asList(
"/Users/liangjia/Downloads/Snipaste_2022-05-18_14-25-25.png",
"/Users/liangjia/Downloads/测试图.png",
"/Users/liangjia/Downloads/Snipaste_2022-05-18_14-25-25.png"
)).build());
healthKits.add(PointPositionForm.builder()
.phoneNum("18888888888")
.idNum("我是身份证号2")
.healthKitGeneratorName("测试姓名2")
.healthKitScreenshots(Arrays.asList(
"/Users/liangjia/Downloads/测试图.png",
"/Users/liangjia/Downloads/Snipaste_2022-05-18_14-25-25.png"
)).build());
List<HealthKitLetterInfo> healthKitLetterInfos = new ArrayList<>();
// 遍历两个实体,将数据添加到word文档模版对应的实体上
for (PointPositionForm healthKit : healthKits) {
HealthKitLetterInfo healthKitLetterInfo = HealthKitLetterInfo.builder()
.healthKitGeneratorName(healthKit.getHealthKitGeneratorName())
.phone(healthKit.getPhoneNum())
.icNumber(healthKit.getIdNum())
.qrCode(new ArrayList<>())
.build();
// 多个健康宝二维码
List<String> healthKitScreenshots = healthKit.getHealthKitScreenshots();
List<Map<String, PictureRenderData>> qrCode = healthKitLetterInfo.getQrCode();
for(String imageUrl : healthKitScreenshots){
Map<String,PictureRenderData> map=new HashMap<>();
map.put("pictures", new PictureRenderData(100, 120, imageUrl));
qrCode.add(map);
}
healthKitLetterInfos.add(healthKitLetterInfo);
}
paramMap.put("section1",healthKitLetterInfos);
// 生成文件
Map<String, String> resultMap = createPdf(paramMap);
}
private static Map<String, String> createPdf(Map<String, Object> paramMap) {
log.info("生成电子协查函参数paramMap = {}", paramMap);
// 文件存放根目录
String rootPath = "/Users/liangjia" + File.separator ;
File dir = new File(rootPath);
// 文件夹不存在则创建
if (!dir.exists()) {
boolean mkdir = dir.mkdir();
if (!mkdir) {
log.error("文件目录创建失败");
}
}
String docxPath = rootPath + "健康宝-同时空协查函" + ".docx";
String imgPath = rootPath + "image" + File.separator;
// 获取电子协查函模板
String path = "file-template" + File.separator + "健康宝-同时空协查函模板.docx";
// 根据模板渲染word、并且生成pdf文件
ClassPathResource classPathResource = new ClassPathResource(path);
try (InputStream inputStream = classPathResource.getInputStream()) {
if (Objects.isNull(inputStream)) {
log.error("获取电子协查函模板失败");
}
// 通过协查函模板,开始生成电子协查函
try (XWPFTemplate template = XWPFTemplate.compile(inputStream).render(paramMap);
OutputStream outDocx = Files.newOutputStream(Paths.get(docxPath))) {
NiceXWPFDocument xwpfDocument = template.getXWPFDocument();
// 输出word
template.getXWPFDocument().write(outDocx);
}
} catch (Exception e) {
log.error("创建协查函异常,异常详情:\n{}", e);
}
Map<String, String> map = new HashMap<>(4);
map.put("rootPath", rootPath);
map.put("docxPath", docxPath);
return map;
}