需求描述:
客户网页上填一个Excel表格,数据存到数据库,这个导出接口要做的就是从数据库中的获取数据并填充到模板文件,最后通过response返给前端一个下载链接,用户即可获取填充好的Excel文件。
方案一:
一开始使用的是easypoi,发现当填充一行数据时是OK的,但是如果是多行数据,处理对象集合会抛异常,所以在小组长的建议下最终放弃选择使用EasyExcel;
方案二:
组长给了个图示 直接看懂 如下两图:
模糊的数据是模板数据不用关注,目的是往空格里填充数据
左边模板,右边结果,一目了然,我觉得挺简单写了个demo也成功了(文件生成在本地)
后面我想着网上找找样例,把输出流设置到response里面应该不难,我自己写好了调试却发现一直报错,各种改都不行,我调试+修改搞了一上午,后面我跟组长汇报了情况,他说他来改,半小时就改好了。
我写的代码是这样的:
<!--EasyExcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>3.1.1</version>
</dependency>
@GetMapping("/exportExcel")
@ApiOperation(value = "导出", notes = "导出", httpMethod = "GET")
@ApiImplicitParams(value = {
@ApiImplicitParam(name = "code", paramType = "query", value = "单位编码", required = true, dataType = "String", example = "12312311")
})
public void exportExcel(@RequestParam String code, HttpServletResponse response)throws Exception {
reportService.exportExcel(code, response);
}
@Override
public void exportExcel(String code, HttpServletResponse response) throws Exception{
//先查询详情列表是否存在
List<ReportDetailDO> reportDetailDOS = reportDetailDAO.select(
ReportDetailDO.builder()
.code(code)
.build());
//不存在时抛异常——未查询到填报记录
if (CollectionUtils.isEmpty(reportDetailDOS)) {
throw new BusException(ResultEnum.NO_FILLED_IN_RECORDS_FOUND);
}
ReportOrgDO reportOrgDO = reportOrgDAO.selectOne(
ReportOrgDO.builder()
.code(code)
.build());
String templateFileName = "template/vnd.xlsx";
// 这里URLEncoder.encode可以防止中文乱码
String fileName = URLEncoder.encode(reportOrgDO.getName() + TABLE_NAME+".xlsx", "UTF-8").replaceAll("\\+", "%20");
//文件名=公司名+表名
response.setCharacterEncoding("UTF-8");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
ServletOutputStream outputStream = response.getOutputStream();
InputStream templateFile = this.getClass().getClassLoader().getResourceAsStream(templateFileName);
ExcelWriter excelWriter = EasyExcel.write(outputStream).withTemplate(templateFile).build();
WriteSheet writeSheet = EasyExcel.writerSheet().build();
//开启自动换行,自动换行表示每次写入一条list数据是都会重新生成一行空行,此选项默认是关闭的,需要提前设置为true
FillConfig fillConfig = FillConfig.builder().forceNewRow(Boolean.TRUE).build();
List<ReportDetailExcelDTO>list=OrikaHelper.convertList(reportDetailDOS, ReportDetailExcelDTO.class);
excelWriter.fill(list, fillConfig, writeSheet);
excelWriter.finish();
}
用户导出过度的类如下:
@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel("汇报详情Excel")
public class ReportDetailExcelDTO {
@ExcelProperty("2019年度")
private String content1;
@ExcelProperty("2020年度")
private String content2;
@ExcelProperty("2021年度")
private String content3;
@ExcelProperty("2022年度")
private String content4;
@ExcelProperty("备注")
private String remark;
}
组长是这样写的:
<!--EasyExcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.10</version>
</dependency>
//省略获取数据对象集合的方法。。。(list)
String templateFileName = "template/vnd.xlsx";
// 这里URLEncoder.encode可以防止中文乱码
String fileName = URLEncoder.encode(reportOrgDO.getName() + TABLE_NAME + ".xlsx", "UTF-8")
.replaceAll("\\+", "%20");
//文件名=公司名+表名
response.setCharacterEncoding("UTF-8");
response.setHeader("content-Type", "application/vnd.ms-excel");
response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));
//ReportDetailDO是数据对象的泛型 相当于数据库的实体类 不需要加特殊的EasyExcel注解
InputStream templateFile = this.getClass().getClassLoader().getResourceAsStream(templateFileName);
EasyExcel.write(response.getOutputStream(), ReportDetailDO.class)
.withTemplate(templateFile).sheet().doFill(Optional.ofNullable(list).orElse(new ArrayList<>()));
组长简简单单改了下依赖的版本,业务代码写了几行然后云淡风轻告诉我可以了,我当时真的很不理解,心情很复杂,一方面觉得自己为什么搞不出来,另外觉得像改变版本这种问题真的很难找到排查出来,经历这件事让我觉得,同样是程序员,有的人还在埋头苦苦调试找原因,有的人轻而易举的可以找到问题并修改,阅历和经验决定了你的实力,自己还是太嫩,路漫漫其修远兮...
另外我发现我用postman居然测不成功,最后用swagger才能返回一个下载链接,无语。。。
这件事给我的启发是,不要新高气傲觉得一切OK的样子,实际上如果网上没有一些开源代码,自己是很难顺利完成某个业务的编写,还是要对技术保持敬畏,自己平时要学会复盘和总结,争取不会被同一个问题难倒两次。另外还要感谢为我们开路的大佬,正所谓前人种树,后人乘凉,respect!