之前导入导出是用POI完成的,导入大量数据的时候会导致内存溢出。导入十四万条数据大搞用了不到二十分钟然后还很容易内存溢出,大佬就推荐使用EasyExcel来实现导入导出。
经过测试使用EasyExcel读十四万条的数据大概需要四十秒左右,写的话没用大量数据测试过,没导出大量数据的需求。
直接看文档的解释:
读
/**
* 文件上传
* <p>1. 创建excel对应的实体对象 参照{@link UploadData}
* <p>2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link UploadDataListener}
* <p>3. 直接读即可
*/
@PostMapping("upload")
@ResponseBody
public String upload(MultipartFile file) throws IOException {
EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead();
return "success";
}
上面这是文档给的demo,下面来撸代码
/**
* 监听器
*/
@Component
@Scope("prototype")
public class OrdersListener extends AnalysisEventListener<OrdersExcelVo> {
@Autowired
OrdersServiceImpl ordersService;
List<Orders> list = new ArrayList<>();
/**
*每读取一行数据会调用的内容
* @param ordersImportVo
* @param analysisContext
*/
@Override
public void invoke(OrdersExcelVo ordersImportVo, AnalysisContext analysisContext) {
//每读取一行数据就传换成数据库需要的类型,添加到列表
list.add(OrdersConvert.convertToOrders(ordersImportVo));
}
@Override
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
System.out.println("表头:"+headMap);
}
/**
* 读取完整个文档后会调用的内容
* @param analysisContext
*/
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
//设置默认允许更新
ordersService.importOrders(list,true);
//清空list
list.clear();
}
}
下面我们来解析一下代码:
- 在文档给的demo中
EasyExcel.read(file.getInputStream(), UploadData.class, new UploadDataListener(uploadDAO)).sheet().doRead();
这行代码中又三个参数:
-
- 第一个参数表示文件的输入流
- 第二个参数指定用那个class去读
- 第三个参数为监听器
- 然后直接
.sheet().doRead()
就行
- 监听器demo
-
- 监听器就是对读到的数据进行操作的地方
- 先指定多例模式
- 监听器中的三个方法都是从文档中复制下来的,注释写的很详细
- 如果嫌
invoke
方法和数据库交互太频繁可以指定一个计数器,每读到十行数据和数据库进行交互一次
-
-
- if(i % 10 == 0)-- 和数据库交互
-
-
- 需要注意的是读完excel调用
doAfterAllAnalysed
方法后需要清空一下list,否则下次读list中还会有上次读的数据
- 需要注意的是读完excel调用
以上这些就是读excel的代码
写
还是直接看文档
/**
* 文件下载(失败了会返回一个有部分数据的Excel)
* <p>
* 1. 创建excel对应的实体对象 参照{@link DownloadData}
* <p>
* 2. 设置返回的 参数
* <p>
* 3. 直接写,这里注意,finish的时候会自动关闭OutputStream,当然你外面再关闭流问题不大
*/
@GetMapping("download")
public void download(HttpServletResponse response) throws IOException {
// 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), DownloadData.class).sheet("模板").doWrite(data());
}
然后撸代码:
public void aMonthPerformance(HttpServletResponse response,AMonthPerformance aMonthPerformance){
List<AMonthPerformance> list = homePageService.aMonthPerformance(aMonthPerformance);
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName= null;
try {
fileName = URLEncoder.encode(String.valueOf(System.currentTimeMillis()) + "部门业绩","UTF-8").replaceAll("\\+","%20");
response.setHeader("Content-disposition","attachment;filename*=utf-8''"+fileName+".xlsx");
EasyExcel.write(response.getOutputStream(),AMonthPerformance.class).sheet("模板").doWrite(list);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
实际写代码的时候直接把文档上的demo复制下来,稍微修改一下就可以了
其中示例代码中的第三行中的 list 是我去数据库中查到的要写入的数据,其中 fileName中我为了文件名不重复用的时间戳 + 部门业绩来当作文件名,其他的用的文档demo中的实例没改
还是来看EasyExcel.write(response.getOutputStream(),AMonthPerformance.class).sheet("模板").doWrite(list);
这行代码,他又两个参数
- 第一个参数为
HttpServletResponse
对象的输出流,用于将数据发送给客户端 - 第二个参数为写入数据的类型,即数据对象的类型。EasyExcel 将使用这个类型来解析数据并生成相应的 Excel 文件。
要生成的excel表格为这种格式:
那我们就继续看文档 写Excel | Easy Excel
/**
* 日期
*/
@ColumnWidth(15)
@JsonFormat(pattern = "yyyy-MM")
@DateTimeFormat("MM月dd日")
@ExcelProperty({"xx组业绩详情日报", "日期"})
private String dataDate;
以上定义数据为其中一个实例,上面有最终效果
实例属性中的注解解释:
@ColumnWidth(15)
宽度指定15(15是什么单位我也不知道。。。格式是我自己调的)@JsonFormat(pattern = "yyyy-MM")
解析时间@ExcelProperty({"xx组业绩详情日报", "日期"})
标题- 其他的顺序啥的文档里都有,没用到就不展示了
可以指定写入的列,因为我定义属性的时候是按照顺序定义的就没写