依赖:
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml-schemas</artifactId>
<version>3.17</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.8.0.M2</version>
</dependency>
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.12.2</version>
</dependency>
<dependency>
<groupId>com.documents4j</groupId>
<artifactId>documents4j-local</artifactId>
<version>1.0.3</version>
</dependency>
<dependency>
<groupId>com.documents4j</groupId>
<artifactId>documents4j-transformer-msoffice-word</artifactId>
<version>1.0.3</version>
</dependency>
模版制作:
说明:
1、为什么要用区块?由于需求是每页6行,每页都要有基本信息表头,所以用区块标签,将表头和表格作为一个区块。区块可循环N次,一个区块就是一页。
2、为什么要分页标记?通过分页标记来插入分页符。
3、复选框格式最重要,一定要是指定的格式(字体格式为Helvetica的复选框),不然打印不出来。
可以来这里复制复选框制作模版:
文件链接:https://pan.baidu.com/s/1Lvo43xkYp6ec2RonHpEeKA
提取码:0617
4、注意在WordUtils.generateWordByTemplate的策略绑定这里使用useSpringEL并且绑定你需要循环的表格占位名;
5、注意{{lookScope=='1'?'☑':'□'}}的单引号,需要半角的,全角的识别不了
成果:
代码:
public void importHouseholdInfo(@RequestParam("householdId") String householdId, HttpServletResponse response) {
File tempFilePath = null;
try {
// 获取户信息
HouseholdPeopleListVo householdPeopleList = peopleService.getHouseholdPeopleList(householdId);
AssertUtil.isNotNull(householdPeopleList, "户信息查询失败");
// 获取户导出模版信息
try (InputStream is = new ClassPathResource("templates/people.docx").getInputStream()) {
// 生成临时文件
tempFilePath = File.createTempFile(String.valueOf(new SnowFlakeIdWorkerUtils(3, 3).nextId()), ".docx");
// 复制模板数据到文件
FileUtils.copyInputStreamToFile(is, tempFilePath);
// 组装导入数据
Map<String, Object> resMap = peopleService.convertImportHouseholdInfo(householdPeopleList);
// 生成 Word 文件
WordUtils.generateWordByTemplate(tempFilePath, resMap, response, "水电工程建设征地实物指标调查表");
} catch (IOException e) {
throw new RuntimeException("模板文件读取失败", e);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 确保临时文件被删除
if (tempFilePath != null) {
tempFilePath.delete();
}
}
}
convertImportHouseholdInfo
public Map<String, Object> convertImportHouseholdInfo(HouseholdPeopleListVo householdPeopleList) {
double page = 1;
if (householdPeopleList.getPeopleList().size() > 6) {
page = Math.ceil((double) householdPeopleList.getPeopleList().size() / 6);
}
List<Map<String, Object>> foreachList = new ArrayList<>();
// 区块标签
Map<String, Object> resMap = new HashMap<>(1);
resMap.put("list", foreachList);
for (int i = 0; i < page; i++) {
List<PeopleInfoVo> parts = new ArrayList<>();
for (int j = 0; j < 6; j++) {
if (i * 6 + j < householdPeopleList.getPeopleList().size()) {
parts.add(householdPeopleList.getPeopleList().get(i * 6 + j));
} else {
break;
}
}
Map<String, Object> materialMap = new HashMap<>();
// 转换表格数据
List<PeopleInfoImportVo> importVoList = parts.stream().map(peopleInfoVo -> {
PeopleInfoImportVo peopleInfoImportVo = new PeopleInfoImportVo();
BeanUtils.copyProperties(peopleInfoVo, peopleInfoImportVo);
peopleInfoImportVo.setLaborForce(peopleInfoVo.getLaborForce() ? "是" : "否");
return peopleInfoImportVo;
}).collect(Collectors.toList());
materialMap.put("tables",importVoList);
materialMap.put("lookScope", String.valueOf(householdPeopleList.getHouseholdInfo().getLookScope()));
materialMap.put("indexRange", String.valueOf(householdPeopleList.getHouseholdInfo().getIndexRange()));
materialMap.put("highRise", String.valueOf(householdPeopleList.getHouseholdInfo().getHighRise()));
// 获取行政区划信息
String adCodePath = householdPeopleList.getHouseholdInfo().getAdCodePath();
String[] split = adCodePath.split("/", -1);
materialMap.put("number", "");
materialMap.put("county", split.length >= 3 ? peopleMapper.selectNameById(split[2]) : "");
materialMap.put("town", split.length >= 4 ? peopleMapper.selectNameById(split[3]) : "");
materialMap.put("village", split.length >= 5 ? peopleMapper.selectNameById(split[4]) : "");
// 获取信息
PeopleInfoVo peopleInfo = householdPeopleList.getPeopleList().stream().filter(peopleInfoVo -> RelationshipEnum.HOUSEHOLDER.getName().equals(peopleInfoVo.getRelationship())).findFirst().orElse(null);
AssertUtil.isNotNull(peopleInfo, "获取信息失败");
materialMap.put("overLap", peopleInfo.getOverlap() ? "1" : "0");
materialMap.put("date",new SimpleDateFormat("yyyy 年 MM 月 dd 日").format(householdPeopleList.getHouseholdInfo().getCreateTime()));
// 插入分页标记,最后一页不用分页
materialMap.put("isPageBreak", i != page - 1 ? "分页标记" : "");
foreachList.add(materialMap);
}
return resMap;
}
WordUtils.generateWordByTemplate
/**
* @param: wordTemplate:模版File;data:填充数据;response:返回response;fileName:下载文件名
*/
public static void generateWordByTemplate(File wordTemplate, Map<String, Object> data, HttpServletResponse response,String fileName) {
try {
// 创建临时文件
File newFile = File.createTempFile(String.valueOf(new SnowFlakeIdWorkerUtils(1, 1).nextId()), ".docx");
// 循环插入策略
LoopRowTableRenderPolicy policy = new LoopRowTableRenderPolicy();
// 策略绑定位置
Configure config = Configure.builder().useSpringEL().bind("tables", policy).build();
// XWPFTemplate读取模板文件并渲染数据
XWPFTemplate render = XWPFTemplate.compile(wordTemplate, config).render(data);
// 替换分页标记为分页符
convertPage(render);
// 渲染完成的数据写入临时文件
render.writeToFile(newFile.getAbsolutePath());
// 将Word文件转换为PDF
File pdfFile = File.createTempFile(String.valueOf(new SnowFlakeIdWorkerUtils(2, 2).nextId()), ".pdf");
try (InputStream docxStream = new FileInputStream(newFile);
OutputStream pdfStream = new FileOutputStream(pdfFile)) {
IConverter converter = LocalConverter.builder().build();
converter.convert(docxStream).as(DocumentType.DOCX).to(pdfStream).as(DocumentType.PDF).execute();
}
// 文件下载
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(fileName + ".pdf", "UTF-8"));
try (FileInputStream in = new FileInputStream(pdfFile);
ServletOutputStream out = response.getOutputStream()) {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
}
// 删除临时文件
newFile.delete();
pdfFile.delete();
} catch (Exception e) {
e.printStackTrace();
}
}
发布线上发现一个问题,word转pdf失败:
可以使用poi转word为pdf,但是我们项目的没有用到的包,最后使用LibreOffice转换的