利⽤EasyExcel导⼊导出多个sheet和多个table
最近项⽬需要导⼊导出多个sheet,并且同⼀个sheet⾥⾯导⼊导出两个或者多个Table,实现过程如下:
Excel导出
导出某个sheet,指定sheet名:
WriteSheet writeSheet = EasyExcel.writerSheet(tableName).build();
指定sheet中的每个表(Table)的表头以及导出对应的实体类,序号0,1分别表⽰第⼏张表,head为指定表头以及表导出对应的实体类:
WriteTable writeTable = EasyExcel.writerTable(0).head(TableExcelData.class).needHead(true).build();
WriteTable writeTable2 = EasyExcel.writerTable(1).head(ItemExcelData.class).needHead(true).build();
这⾥我的TableExcelData.class和ItemExcelData.class分别是导出Excel的实体,其中⼀个定义如下。这⾥注意⽤EasyExcel导出
时,@NoArgsConstructor是必须标注的,不然会报错;@ContentStyle(dataFormat = 49)主要指定导出
时间时Excel单元格格式为⽂本格式,不然为常规格式的话,“yyyy-MM-dd HH:mm:ss"在excel中点击后会变成"yyyy/MM/dd HH:mm:ss”,不利于格式的统⼀。
@AllArgsConstructor
@NoArgsConstructor
@Data
@ToString
@Builder(toBuilder =true)
@ApiModel(value ="Excel实体", description ="数据信息")
public class InspectionTableExcelData {
@ExcelProperty(value ="表名称")
@NotNull
private String tableName;
@ExcelProperty(value ="类型名称")
@NotNull
private String typeName;
@ExcelProperty(value ="创建⼈姓名")
@NotNull
private String creatorName;
@ExcelProperty(value ="表建⽴时间", converter = InstantConverter.class)
@NotNull
@ContentStyle(dataFormat =49)
private Instant tableCreationTime;
}
下⾯是对每个sheet的每个Table进⾏写⼊:
excelWriter.write(ImmutableList.of(inspectionTableExcelData), writeSheet, writeTable);
excelWriter.write(inspectionItemExcelDataList, writeSheet, writeTable2);
ExcelWriter excelWriter = null;
try{
excelWriter = EasyExcel.OutputStream(fileName, response)).build();
for(Long id:idList){
WriteSheet writeSheet = EasyExcel.TableName()).build();
WriteTable writeTable = EasyExcel.writerTable(0).head(TableExcelData.class).needHead(true).build();
WriteTable writeTable2 = EasyExcel.writerTable(1).head(ItemExcelData.class).needHead(true).build();
excelWriter.write(ImmutableList.of(tableExcelData), writeSheet, writeTable);
excelWriter.write(itemExcelDataList, writeSheet, writeTable2);
}
}catch(Exception e){
throw new MMSException(FAILED_Code(),"导出表失败",FAILED_Msg());
}finally{
if(excelWriter!=null){
excelWriter.finish();
}
}
Excel导⼊
导⼊相对复杂些,需要实现如下同⼀个sheet导⼊两个Table,第⼀个Table只有⼀项,第⼀个Table有若⼲项,经过多番查阅和实验最终得到结果:
主要有以下⼏点:
dest为需要读取的⽂件File,file为上传的MultiFile类⽂件,这⾥需要扩展AnalysisEventListener并对⼀些⽅法进⾏重写,特别是需要⾃⼰实现保存数据的⽅法,需要⾃⼰传⼊⼀些mapper或者converter之类。
以下是通过读取dest⽂件,从⽽获得我们上传⽂件的sheet的List;
List<ReadSheet> readSheetList = ad(dest).build().excelExecutor().sheetList();
对于List⾥⾯的每个ReadSheet也就是对于每个sheet,我们进⾏⼀⼀导⼊,如下,通过SheetName())读取某个sheet,通过指定java spring需要匹配的实体类(TableExcelData.class)以及我们的Listener。
.doRead();
.headRowNumber(3)
.doRead();
headRowNumber(3)表⽰我们第⼀个Table从Excel的第3⾏为表头开始读,这⾥有个问题,就是我们在读取sheet的时候,EasyExcel默认是读取全部的,因此在读第⼀个Table的同时也会将第⼆个Table包括表头和内容都读⼊,并且⽤第⼀个表的class进⾏匹配,显⽽易见,这样会出错。因此我们需要指定第⼀个Table的停⽌读取的逻辑,由于我第⼀个Table只需要读⼀项,因此当读取到第⼀项就需要停⽌读取。⽹上查阅资料并没有发现EasyExcel怎么能够实现这个功能,通过去读EasyExcel的中 ReadListener发现其有个hasNext⽅法,其⽅法解释为,我们可以通过返回false从⽽让EasyExcel停⽌读取。这⾥因为我们第⼀个Table只读取⼀项就停⽌了。因此我们可以在继承的Listener上实现hasNext
()⽅法,这⾥通过判断我们⽤于临时存储我们实体的⼀个list,如果list的size等于1了,就令hasNext返回false。
/**
* Verify that there is another piece of data.You can stop the read by returning false
*
* @param context
* @return
*/
boolean hasNext(AnalysisContext context);
}
Listener完整代码如下:
@Component
public class TableExcelListener extends AnalysisEventListener<TableExcelData>{
private static final Logger LOGGER = Logger(TableExcelListener.class);
private static final int BATCH_COUNT =5;
List<TableExcelData> list =new ArrayList<>();
private TableMapper tableMapper;
private TableConverter tableConverter;
private TableExcelConverter tableExcelConverter;
public TableExcelListener(TableMapper tableMapper, TableConverter tableConverter, TableExcelConverter tableExcelConverter){ this.tableMapper = tableMapper;
this.tableConverter = tableConverter;
this.tableExcelConverter = tableExcelConverter;
}
// ⼀条⼀条数据解析 invoke()⽅法
@Override
public void invoke(TableExcelData tableExcelData, AnalysisContext analysisContext){
list.add(tableExcelData);
if(list.size()>=BATCH_COUNT){
saveData();
list.clear();
}
}
@Override
public void onException(Exception exception, AnalysisContext context){
<("解析失败,但是继续解析下⼀⾏:{}", Message());
// 如果是某⼀个单元格的转换异常能获取到具体⾏号
// 如果要获取头的信息配合invokeHeadMap使⽤
if(exception instanceof ExcelDataConvertException){
ExcelDataConvertException excelDataConvertException =(ExcelDataConvertException)exception;
<("第{}⾏,第{}列解析异常", RowIndex(),getsavefilename
// ASException为⾃定义异常
exception.printStackTrace;
}
}
/
/ 所有数据解析完, doAfterAllAnalysed()⽅法,⾥⾯写的有保存数据⽅法
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext){
saveData();
LOGGER.info("所有数据解析完成!");
}
// 业务逻辑,实现保存数据的⽅法
private void saveData(){
for(TableExcelData tableExcelData:list){
try{
TableDTO tableDTO = DTO(tableExcelData);
TableDO tableDO = DO(tableDTO);
TableDO tableDO = DO(tableDTO);
this.tableMapper.addTable(tableDO);
}catch(Exception e){
e.printStackTrace;
}
LOGGER.info("{}条数据,开始存储数据库!",list.size());
LOGGER.info("存储数据库成功!");
}
}
// 停⽌条件,如果list元素为1个时,停⽌读取数据
@Override
public boolean hasNext(AnalysisContext analysisContext){
if(list.size()>=1){
return false;
}
return true;
}
}
最后贴⼀下实现上传的代码:
public int uploadTableExcel(MultipartFile file){
String fileName = OriginalFilename();
/
/ TODO 怎么处理路径不⽣成临时⽂件
String tmpPath = Property("user.dir")+"/tmpPath/";
File dest =new File(tmpPath+fileName);
if(!ParentFile().exists()){
}
try{
// ⽤来把 MultipartFile 转换成 File
}catch(Exception e){
e.printStackTrace;
}
try{
List<ReadSheet> readSheetList = ad(dest).build().excelExecutor().sheetList();
for(ReadSheet readSheet:readSheetList){
.doRead();
.headRowNumber(3)
.doRead();
}
}catch(Exception e){
e.printStackTrace;
}
}
参考⽂章:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论