poi处理多选框进行勾选操作下载word以及多word文件压缩
一、场景
将数据导出word后且实现动态勾选复选框操作
eg: word模板
导出后效果
(根据数据动态勾选复选框)
二、解决方案及涉及技术
① 使用poi提供的库进行处理(poi官方文档)
② 涉及依赖
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<!-- pio处理word文件操作复选框-->
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.9.1</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-scratchpad</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>ooxml-schemas</artifactId>
<version>1.4</version>
</dependency>
三、代码实现
word单个/批量下载工具类
在操作模板时,我们需先将写入word模板的数据构建为Map
① 数据封装处理(TrafficBlock 对象)
public Map buildTrafficDataForm(TrafficBlock trafficBlock){
BlockDownLoadDataForm dataForm = new BlockDownLoadDataForm();
BeanUtils.copyProperties(trafficBlock, dataForm);
dataForm.setDirection(EventInfoDirection.getDirection(trafficBlock.getDirection()).getMessage());
// 涉桥/涉隧
dataForm.setInvolveNo(String.valueOf(trafficBlock.getInvolveNumber()));
// 模板数据处理
dataForm.setStartToEndPile("K"+trafficBlock.getStartKilometersPile()+"+"+trafficBlock.getStartHectometerPile()+"至"+trafficBlock.getEndKilometersPile()+"+"+trafficBlock.getEndHectometerPile());
dataForm.setAffectedStartToEndPile("K"+trafficBlock.getAffectedStartKilometersPile()+"+"+trafficBlock.getAffectedStartHectometerPile()+"至"+trafficBlock.getAffectedEndKilometersPile()+"+"+trafficBlock.getAffectedEndHectometerPile());
// 阻断类别(突发类才会有)
String blockCategory = String.valueOf(trafficBlock.getBlockCategory());
// checkBox处理
if("1".equals(trafficBlock.getBlockNature())){
// 计划类
dataForm.setPlanCheckBox(String.valueOf(trafficBlock.getBlockType()));
}else{ //突发类
if("1".equals(blockCategory)){
// 地质灾害
dataForm.setGeologyCheckBox(String.valueOf(trafficBlock.getBlockType()));
}else if("2".equals(blockCategory)){
// 重大灾害
dataForm.setGreatCheckBox(String.valueOf(trafficBlock.getBlockType()));
}else if("3".equals(blockCategory)){
// 气象灾害
dataForm.setWeatherCheckBox(String.valueOf(trafficBlock.getBlockType()));
}else if("4".equals(blockCategory)){
// 事故灾害
dataForm.setAccidentCheckBox(String.valueOf(trafficBlock.getBlockType()));
}else if("5".equals(blockCategory)){
// 其他
dataForm.setOtherCheckBox(String.valueOf(trafficBlock.getBlockType()));
}
}
return convertTrafficBlockToMap(dataForm);
}
② dataMap构建工具
public static Map<String, String> convertTrafficBlockToMap(BlockDownLoadDataForm downLoadDataForm) {
Map<String, String> valueMap = new HashMap<>();
Class<?> clazz = downLoadDataForm.getClass();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
try {
Object fieldValue = field.get(downLoadDataForm);
if (fieldValue == null) {
valueMap.put(field.getName(), "");
} else {
valueMap.put(field.getName(), String.valueOf(fieldValue));
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
return valueMap;
}
此处需要注意在多个文件下载时,每次向ZipOutputStream 写入字节流时,需要为每个生成的 Word 文件提供一个唯一的名称(写入的文件名必须不一致)否则会导致每次写入的流覆盖之前的,导致浏览器不能正确解析,进而下载失败!!!
// *****(Word单个/批量下载)
public void generateTrafficWordForm(HttpServletResponse response, List<Long> ids) throws IOException {
List<TrafficBlock> trafficBlocks = trafficBlockMapper.selectList(new LambdaQueryWrapper<TrafficBlock>().in(TrafficBlock::getId, ids));
if (ids.size() == 1){
// 单个下载
Map dataMap = buildTrafficDataForm(trafficBlocks.get(0));
response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=***记录表.docx");
response.setContentType(String.valueOf(MediaType.APPLICATION_OCTET_STREAM));
try (OutputStream out = response.getOutputStream()) {
writeTrafficWordForm(dataMap, out);
}
}else if (ids.size() > 1) {
// 多个文件压缩下载
response.setContentType("application/zip");
response.setCharacterEncoding("UTF-8");
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode("交通阻断.zip", "UTF-8"));
try (ZipOutputStream zipOut = new ZipOutputStream(response.getOutputStream())) {
for (TrafficBlock trafficBlock : trafficBlocks) {
Map dataMap = buildTrafficDataForm(trafficBlock);
ByteArrayOutputStream wordStream = new ByteArrayOutputStream();
writeTrafficWordForm(dataMap, wordStream);
// 为每个文件生成唯一的名称
String uniqueFileName = "*****_" + trafficBlock.getId() + ".docx";
zipOut.putNextEntry(new ZipEntry(uniqueFileName));
zipOut.write(wordStream.toByteArray());
zipOut.closeEntry();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
poi工具类读取模板处理数据工具:
public void writeTrafficWordForm(Map<String, String> dataMap,OutputStream outputStream) {
/**
* 复选框
* */
//需要循环的表单数据
dataMap.put("dataTable", String.valueOf(new ArrayList<>()));
ConfigureBuilder configureBuilder = Configure.builder().useSpringEL().bind("dataTable", new HackLoopTableRenderPolicy());
Configure config = configureBuilder.build();
InputStream is = null;
try {
// 读取Word模板文件,获取输入流
is = new ClassPathResource("template/profile/交通阻断记录表.docx").getInputStream();
XWPFTemplate template = XWPFTemplate.compile(is, config).render(dataMap);
template.write(outputStream);
outputStream.flush();
PoitlIOUtils.closeQuietlyMulti(template, outputStream);
} catch (IOException e) {
log.error("失败!!!!!!", e);
} finally {
if (null != is) {
try {
is.close();
} catch (IOException e) {
log.error("关闭流失败!", e);
}
}
}
}