工作中用到excel的多sheet页数据的读取和下载多sheet页的数据,之前都是手动一个一个表格自己拼装数据.最近研究了EasyExcel,话不多说直接上代码
1.导入sheet页面的实体类
@Data
public class ReqCustomerDailyImport {
/**
* 客户名称
*/
@ExcelProperty(index = 0)
private String customerName;
/**
* MIS编码
*/
@ExcelProperty(index = 1)
private String misCode;
/**
* 月度滚动额
*/
@ExcelProperty(index = 3)
private BigDecimal monthlyQuota;
/**
* 最新应收账款余额
*/
@ExcelProperty(index = 4)
private BigDecimal accountReceivableQuota;
/**
* 本月利率(年化)
*/
@ExcelProperty(index = 5)
private BigDecimal dailyInterestRate;
}
2.导出sheet页面的实体类
@Data
//@Builder
public class RespCustomerDailyImport {
@ExcelProperty("客户编码")
private String customerName;
@ExcelProperty("MIS编码")
private String misCode;
@ExcelProperty("月度滚动额")
private BigDecimal monthlyQuota;
@ExcelProperty("最新应收账款余额")
private BigDecimal accountReceivableQuota;
@NumberFormat("#.##%")
@ExcelProperty("本月利率(年化)")
private BigDecimal dailyInterestRate;
}
3.导入和导出的controller
@Controller
@Api(tags = "ExcelController", description = "excel读写")
@RequestMapping("/excel")
public class ExcelController {
@PostMapping("/singleImport")
public void importCustomerDaily(@RequestParam MultipartFile file) throws IOException {
InputStream inputStream = file.getInputStream();
List<ReqCustomerDailyImport> reqCustomerDailyImports = EasyExcel.read(inputStream)
.head(ReqCustomerDailyImport.class)
// 设置sheet,默认读取第一个
.sheet()
// 设置标题所在行数
.headRowNumber(2)
.doReadSync();
System.out.println(reqCustomerDailyImports);
}
@PostMapping("/MultiImport")
public void MultiImportCustomerDaily(@RequestParam MultipartFile file) throws IOException {
InputStream inputStream = file.getInputStream();
//新建监听器
ExcelListener listener = new ExcelListener();
ExcelReader excelReader = EasyExcel.read(inputStream, listener).build();
// 第一个sheet读取类型
ReadSheet readSheet1 = EasyExcel.readSheet(0).head(ReqCustomerDailyImport.class).build();
// 第二个sheet读取类型
ReadSheet readSheet2 = EasyExcel.readSheet(1).head(ReqCustomerDailyImport.class).build();
// 开始读取第一个sheet
excelReader.read(readSheet1);
List<Object> list = listener.getDatas();
list.forEach((user) -> {
ReqCustomerDailyImport import1 = (ReqCustomerDailyImport) user;
System.out.println(import1.getCustomerName() + ", " + import1.getMisCode());
});
// 清空之前的数据
listener.getDatas().clear();
System.out.println("---------------------------------");
// 开始读取第二个sheet
excelReader.read(readSheet2);
List<Object> list2 = listener.getDatas();
list2.forEach((user) -> {
ReqCustomerDailyImport import2 = (ReqCustomerDailyImport) user;
System.out.println(import2.getCustomerName() + ", " + import2.getMisCode());
});
}
@GetMapping("/singleExport")
public void export(HttpServletResponse response) throws IOException {
// 生成数据
List<RespCustomerDailyImport> respCustomerDailyImports = Lists.newArrayList();
for (int i = 0; i < 50; i++) {
RespCustomerDailyImport model = new RespCustomerDailyImport();
model.setCustomerName("customerName" + i);
model.setMisCode(String.valueOf(i));
model.setMonthlyQuota(new BigDecimal(String.valueOf(i)));
model.setAccountReceivableQuota(new BigDecimal(String.valueOf(i)));
model.setDailyInterestRate(new BigDecimal(String.valueOf(i)));
respCustomerDailyImports.add(model);
}
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("test", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
EasyExcel.write(response.getOutputStream(), RespCustomerDailyImport.class)
.sheet("sheet0")
// 设置字段宽度为自动调整,不太精确
.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
.doWrite(respCustomerDailyImports);
}
@GetMapping("/MultiExport")
public void MultiExport(HttpServletResponse response) throws IOException {
// 生成数据
List<RespCustomerDailyImport> respCustomerDailyImports = Lists.newArrayList();
for (int i = 0; i < 50; i++) {
RespCustomerDailyImport model = new RespCustomerDailyImport();
model.setMisCode(String.valueOf(i));
model.setCustomerName("customerName" + i);
model.setMonthlyQuota(new BigDecimal(String.valueOf(i)));
model.setDailyInterestRate(new BigDecimal(String.valueOf(i)));
model.setAccountReceivableQuota(new BigDecimal(String.valueOf(i)));
respCustomerDailyImports.add(model);
}
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
String fileName = URLEncoder.encode("test", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
// 测试多sheel导出
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream()).build();
WriteSheet test1 = EasyExcel.writerSheet(0, "sheet0").head(RespCustomerDailyImport.class).build();
WriteSheet test2 = EasyExcel.writerSheet(1, "sheet1").head(RespCustomerDailyImport.class).build();
excelWriter.write(respCustomerDailyImports, test1).write(respCustomerDailyImports, test2);
excelWriter.finish();
}
}
4.由于导入excel 读取多sheet页的数据时需要自己写个Listener 代码如下
//此Listener 不能被Spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class ExcelListener extends AnalysisEventListener<Object> {
private static final Logger LOGGER = LoggerFactory.getLogger(ExcelListener.class);
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 30000;
List<Object> list = new ArrayList<>();
public ExcelListener() {
}
/**
* 这个每一条数据解析都会来调用
*
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(Object data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));
list.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (list.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
list.clear();
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
LOGGER.info("所有数据解析完成!");
}
/**
* 获取数据
* @return
*/
public List<Object> getDatas() {
return list;
}
/**
* 加上存储数据库
*/
private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size());
// demoDAO.save(list);
LOGGER.info("存储数据库成功!");
}
// /**
// * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
// */
// private DemoDAO demoDAO;
// public ExcelListener() {
// // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
// demoDAO = new DemoDAO();
// }
// /**
// * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
// *
// * @param demoDAO
// */
// public ExcelListener(DemoDAO demoDAO) {
// this.demoDAO = demoDAO;
// }
}