利用EasyExcel导入导出多个sheet和多个table

7 篇文章 0 订阅

最近项目需要导入导出多个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.classItemExcelData.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.write(ExcelUtil.getOutputStream(fileName, response)).build();
            for(Long id:idList) {

                WriteSheet writeSheet = EasyExcel.writerSheet(tableExcelData.getTableName()).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_TO_EXPORT.getCode(),"导出表失败",FAILED_TO_EXPORT.getMsg());
        }finally {
            if (excelWriter!=null){
                excelWriter.finish();
            }
        }

Excel导入

导入相对复杂些,需要实现如下同一个sheet导入两个Table,第一个Table只有一项,第一个Table有若干项,经过多番查阅和实验最终得到结果:
在这里插入图片描述
主要有以下几点:
dest为需要读取的文件Filefile为上传的MultiFile类文件,这里需要扩展AnalysisEventListener并对一些方法进行重写,特别是需要自己实现保存数据的方法,需要自己传入一些mapper或者converter之类。
以下是通过读取dest文件,从而获得我们上传文件的sheetList

List<ReadSheet> readSheetList = EasyExcel.read(dest).build().excelExecutor().sheetList();

对于List里面的每个ReadSheet也就是对于每个sheet,我们进行一一导入,如下,通过sheet(readSheet.getSheetName())读取某个sheet,通过指定java spring需要匹配的实体类(TableExcelData.class)以及我们的Listener

EasyExcel.read(dest, TableExcelData.class, new TableExcelListener<TableExcelData>(tableMapper,tableConverter, tableExcelConverter))
          .sheet(readSheet.getSheetName())
          .doRead();
EasyExcel.read(dest, ItemExcelData.class, new TableExcelListener<ItemExcelData>(ItemMapper, itemConverter,itemExcelConverter))
          .sheet(readSheet.getSheetName())
          .headRowNumber(3)
          .doRead();

headRowNumber(3)表示我们第一个TableExcel的第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 = LoggerFactory.getLogger(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) {
        LOGGER.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
        // 如果是某一个单元格的转换异常 能获取到具体行号
        // 如果要获取头的信息 配合invokeHeadMap使用
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
            LOGGER.error("第{}行,第{}列解析异常", excelDataConvertException.getRowIndex(),
                    excelDataConvertException.getColumnIndex());
            // ASException为自定义异常
            exception.printStackTrace;
        }
    }
    // 所有数据解析完, doAfterAllAnalysed()方法,里面写的有保存数据方法
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext){
        saveData();
        LOGGER.info("所有数据解析完成!");
    }
    
    // 业务逻辑,实现保存数据的方法
    private void saveData(){
        for(TableExcelData tableExcelData:list){

        try{
            TableDTO tableDTO = tableExcelConverter.toDTO(tableExcelData);
            TableDO tableDO = tableConverter.toDO(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 = file.getOriginalFilename();

        // TODO 怎么处理路径 不生成临时文件
        String tmpPath = System.getProperty("user.dir") + "/tmpPath/";
        File dest = new File(tmpPath+fileName);
        if(!dest.getParentFile().exists()){
            dest.getParentFile().mkdirs();
        }

        try{
            // 用来把 MultipartFile   转换成  File
            file.transferTo(dest);
        } catch (Exception e) {
            e.printStackTrace;
        }
        
        try {
            List<ReadSheet> readSheetList = EasyExcel.read(dest).build().excelExecutor().sheetList();
            for(ReadSheet readSheet:readSheetList) {
                EasyExcel.read(dest, TableExcelData.class, new TableExcelListener<TableExcelData>(tableMapper,tableConverter, tableExcelConverter))
                        .sheet(readSheet.getSheetName())
                        .doRead();
                EasyExcel.read(dest, ItemExcelData.class, new TableExcelListener<ItemExcelData>(ItemMapper, itemConverter,itemExcelConverter))
                        .sheet(readSheet.getSheetName())
                        .headRowNumber(3)
                        .doRead();
            }
        } catch (Exception e) {
            e.printStackTrace;
        }
}

参考文章:
easyexcel 使用table写入
easyexcel源码
急!请问EasyExcel如何获取sheetname?
EasyExcel从指定位置开始读数据
EasyExceld读取流程图
官方文档
Easyexcel github

  • 19
    点赞
  • 101
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
对于 EasyExcel导入导出功能,你可以使用 EasyExcel 库来实现。下面是一个简单的示例代码,用于演示如何使用 EasyExcel 进行导入导出操作: ```java import com.alibaba.excel.EasyExcel; import com.alibaba.excel.ExcelWriter; import com.alibaba.excel.metadata.Sheet; import com.alibaba.excel.metadata.Table; import com.alibaba.excel.support.ExcelTypeEnum; import java.util.ArrayList; import java.util.List; public class ExcelUtils { public static void main(String[] args) { // 导出Excel文件 exportExcel(); // 导入Excel文件 importExcel(); } public static void exportExcel() { // 准备数据 List<User> userList = new ArrayList<>(); userList.add(new User("张三", 20)); userList.add(new User("李四", 25)); // 设置表格标题 String[] headers = {"姓名", "年龄"}; // 创建表格对象 Table table = new Table(0); table.setTableStyle(createTableStyle()); table.setHead(headers); // 设置写入Excel的文件路径和文件名 String fileName = "user.xlsx"; // 写入Excel文件 ExcelWriter excelWriter = EasyExcel.write(fileName).withWriterSheet().build(); Sheet sheet = new Sheet(1, 0, User.class); sheet.setTableStyle(createTableStyle()); excelWriter.write(userList, sheet); excelWriter.finish(); } public static void importExcel() { // 读取Excel文件 String fileName = "user.xlsx"; List<User> userList = EasyExcel.read(fileName).head(User.class).sheet().doReadSync(); // 处理导入的数据 for (User user : userList) { System.out.println("姓名: " + user.getName() + ", 年龄: " + user.getAge()); } } private static TableStyle createTableStyle() { // 创建表格样式对象 TableStyle tableStyle = new TableStyle(); tableStyle.setTableContentBackGroundColor(IndexedColors.WHITE); tableStyle.setTableContentFontName("宋体"); tableStyle.setTableContentFontSize((short) 9); return tableStyle; } // 用户类 public static class User { private String name; private int age; public User(String name, int age) { this.name = name; this.age = age; } // getter 和 setter 方法 // ... } } ``` 在上面的代码中,`exportExcel` 方法用于导出 Excel 文件,将用户列表数据写入 Excel 中。`importExcel` 方法用于导入 Excel 文件,读取 Excel 中的数据并进行处理。你可以根据自己的需求进行修改和扩展。在使用 EasyExcel 进行导入导出操作时,需要引入相应的库依赖。如果使用 Maven 进行项目管理,可以在 `pom.xml` 文件中添加以下依赖: ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.10</version> </dependency> ``` 这样就可以使用 EasyExcel导入导出功能了。希望对你有所帮助!如果有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值