EasyExcel practice and notes

overview

Excel import and export is a very common requirement in business development. This article records how to quickly start using EasyExcel, in-depth actual combat, and problems encountered.

getting Started

Use EasyExcel to import the following dependencies:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
</dependency>

There are mainly two types of scenarios for using EasyExcel:

  1. Upload the Excel file and analyze the data in the Sheet, data verification, dirty data clearing, data storage, etc.;
  2. After querying the data from the database, download and export it to Excel;

Generally speaking, when implementing the above two requirement scenarios, a tool class will be provided

/**
 * excel 导出
 *
 * @param fileName         fileName
 * @param fileNameTemplate 需要提供模板
 * @param data             list<PO>
 * @throws IOException 调用方处理
 */
public static ResponseEntity<Resource> excelExport(String fileName, String fileNameTemplate, Object data) throws IOException {
    
    
    /*
     * 填充文件 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替 {} 代表普通变量 {.} 代表是list的变量
     */
    InputStream inputStream = getResourcesFileInputStream(fileNameTemplate);
    fileName = fileName + ".xlsx";
    ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(inputStream).build();
    WriteSheet writeSheet = EasyExcel.writerSheet().build();
    writeSheet.setSheetName(fileName);

    FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.VERTICAL).build();
    excelWriter.fill(data, fillConfig, writeSheet);
    // 关闭流
    excelWriter.finish();

    File dbfFile = new File(fileName);
    if (!dbfFile.exists()) {
    
    
        dbfFile.createNewFile();
    }
    HttpHeaders headers = new HttpHeaders();
    headers.add("Content-disposition", "attachment; filename=" + URLEncoder.encode(fileName, StandardCharsets.UTF_8));

    InputStreamResource resource = new InputStreamResource(new FileInputStream(dbfFile));

    return ResponseEntity.ok().headers(headers).contentLength(dbfFile.length()).contentType(MediaType.parseMediaType("application/octet-stream;charset=UTF-8")).body(resource);
}

private static InputStream getResourcesFileInputStream(String fileName) {
    
    
    return Thread.currentThread().getContextClassLoader().getResourceAsStream("" + fileName);
}

upload

export

The Controller layer interface uses the above excelExport export method:

@ApiOperation(value = "数据统计服务-数据概览-性别", notes = "数据概览-性别")
@PostMapping("/overview/sex/excel")
public ResponseEntity<Resource> overviewSex(HttpServletResponse response, @RequestBody CommonQueryParam param) throws IOException {
    
    
	// 仅作为示例,一般不会在controller层写业务biz逻辑
    List<SexExcel> sexExcelList = getList(param);	
   return ExcelUtils.excelExport("性别数据", "sexTemplate.xlsx", sexExcelList);
}

Then provide a sexTemplate.xlsx template file, put it under the classpath path, generally src/main/resourcesunder the path:

insert image description here
The definition of the POJO entity class corresponding to the Excel template file is as follows:

@Data
public class SexExcel {
    
    
    private String date;
    private String source;
    private String sex;
    private Long number;
}

It should be noted that the controller layer interface request parameters must have HttpServletResponse response, and the return type must beResponseEntity<Resource>

Basically, the entry-level use is the above content.

Advanced

Dynamically add self-incrementing serial number column

The effect you want to achieve:
insert image description here
to implement the scheme, add a custom RowWriteHandler:

private static class CustomRowWriteHandler implements RowWriteHandler {
    
    
    private static final String FIRST_CELL_NAME = "序号";
    /**
     * 列号
     */
    private int count = 1;

    @Override
    public void beforeRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Integer integer, Integer integer1, Boolean aBoolean) {
    
    
    }

    @Override
    public void afterRowCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer integer, Boolean aBoolean) {
    
    
        Cell cell = row.createCell(0);
        if (row.getRowNum() == 0) {
    
    
            cell.setCellValue(FIRST_CELL_NAME);
        }
        cell.setCellValue(++count);
    }

    @Override
    public void afterRowDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Integer integer, Boolean aBoolean) {
    
    
    }
}

Register this RowWriteHandler:

ExcelWriter excelWriter = EasyExcel.write(fileName).withTemplate(inputStream).registerWriteHandler(new CustomRowWriteHandler()).build();

question

Record several problems encountered when using EasyExcel.

NoClassDefFoundError: org/apache/poi/util/DefaultTempFileCreationStrategy

Background: The Shishan code written by a colleague who has left the company before uses the native poi to export Excel. There are too many problems, including but not limited to interface timeout, CPU soaring, etc.
insert image description here
There is also an error reporting that the stream is closed abnormally: failed UT010029: Stream is closed, because it is used when printing the log e.getMessage(), and the error stack is not printed.

There are not too many problems about native POI on the Internet, including office 2007 and 2010 adaptation, high memory usage, CPU usage, lengthy code, etc.

So, I had the idea of ​​replacing native POI with EasyExcel. Import EasyExcel, Debug mode fails to start the application, the detailed error information is as follows:

Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: org/apache/poi/util/DefaultTempFileCreationStrategy
org.springframework.web.util.NestedServletException: Handler dispatch failed; nested exception is java.lang.NoClassDefFoundError: org/apache/poi/util/DefaultTempFileCreationStrategy
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1054)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:942)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1005)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:897)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:645)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:882)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:750)
at io.undertow.servlet.handlers.ServletHandler.handleRequest(ServletHandler.java:74)

For very common errors, refer to NoClassDefFoundError, ClassNotFoundException, NoSuchMethodError in Java learning . NoClassDefFoundError is generally caused by dependency conflicts when multiple versions of three-party jar packages coexist.

Check pom.xmlthe file, before using the native POI export to import the version number:

<dependency>
    <groupId>org.apache.poi</groupId>
    <artifactId>poi-ooxml</artifactId>
    <version>3.11</version>
</dependency>

The EasyExcel-2.2.11 introduced by the transformation, the dependent version is:
insert image description here
delete poi-3.11the version to solve the problem.

ExcelGenerateException: Calling the ‘fill’ method must use a template

The error log is:

at com.alibaba.excel.write.ExcelBuilderImpl.fill(ExcelBuilderImpl.java:72)
at com.alibaba.excel.ExcelWriter.fill(ExcelWriter.java:185)
at com.aba.common.utils.ExcelUtils.excelExport(ExcelUtils.java:47)

The Excel template is defined, and the POJO entity class is also defined. However, when coding the business, you will find that another spelling is more suitable. For example, when the two sides are inconsistent, there will be this problem name.alias

@Data
public class DaTongUser {
    
    
    private Integer index;
    private String name;
    private String mobile;
}

Corresponding Excel template file:
insert image description here

NullPointerException: null

Specific error message:

at com.alibaba.excel.write.executor.ExcelWriteFillExecutor.doFill(ExcelWriteFillExecutor.java:191)
at com.alibaba.excel.write.executor.ExcelWriteFillExecutor.fill(ExcelWriteFillExecutor.java:118)
at com.alibaba.excel.write.ExcelBuilderImpl.fill(ExcelBuilderImpl.java:78)
at com.alibaba.excel.ExcelWriter.fill(ExcelWriter.java:185)
at com.aba.common.utils.ExcelUtils.excelExport(ExcelUtils.java:47)

The version numbers used are:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.2.11</version>
</dependency>

The source code of the error is as follows:

public void fill(Object data, FillConfig fillConfig) {
    
    
    if (data == null) {
    
    
        data = new HashMap<String, Object>(16);
    }
    if (fillConfig == null) {
    
    
        fillConfig = FillConfig.builder().build(true);
    }
    fillConfig.init();

    Object realData;
    if (data instanceof FillWrapper) {
    
    
        FillWrapper fillWrapper = (FillWrapper) data;
        currentDataPrefix = fillWrapper.getName();
        realData = fillWrapper.getCollectionData();
    } else {
    
    
        realData = data;
        currentDataPrefix = null;
    }
    currentUniqueDataFlag = uniqueDataFlag(writeContext.writeSheetHolder(), currentDataPrefix);

    // processing data
    if (realData instanceof Collection) {
    
    
    	// 报错根源
        List<AnalysisCell> analysisCellList = readTemplateData(templateCollectionAnalysisCache);
        Collection collectionData = (Collection) realData;
        if (CollectionUtils.isEmpty(collectionData)) {
    
    
            return;
        }
        Iterator iterator = collectionData.iterator();
        if (WriteDirectionEnum.VERTICAL.equals(fillConfig.getDirection()) && fillConfig.getForceNewRow()) {
    
    
            shiftRows(collectionData.size(), analysisCellList);
        }
        while (iterator.hasNext()) {
    
    
        	// 报错方法
            doFill(analysisCellList, iterator.next(), fillConfig, getRelativeRowIndex());
        }
    } else {
    
    
        doFill(readTemplateData(templateAnalysisCache), realData, fillConfig, null);
    }
}
private List<AnalysisCell> readTemplateData(Map<String, List<AnalysisCell>> analysisCache) {
    
    
    List<AnalysisCell> analysisCellList = analysisCache.get(currentUniqueDataFlag);
    if (analysisCellList != null) {
    
    
        return analysisCellList;
    }
    Sheet sheet = writeContext.writeSheetHolder().getCachedSheet();
    Map<String, Set<Integer>> firstRowCache = new HashMap<String, Set<Integer>>(8);
    for (int i = 0; i <= sheet.getLastRowNum(); i++) {
    
    
        Row row = sheet.getRow(i);
        if (row == null) {
    
    
            continue;
        }
        for (int j = 0; j < row.getLastCellNum(); j++) {
    
    
            Cell cell = row.getCell(j);
            if (cell == null) {
    
    
                continue;
            }
            String preparedData = prepareData(cell, i, j, firstRowCache);
            // Prevent empty data from not being replaced
            if (preparedData != null) {
    
    
                cell.setCellValue(preparedData);
            }
        }
    }
    return analysisCache.get(currentUniqueDataFlag);
}

I can't understand what I'm doing. Time does not allow, did not study hard.

I can't find relevant information in the GitHub issue. Put the upgraded version number of the dependency to 3.0.1, and report an error:

java.lang.RuntimeException: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class java.io.FileDescriptor and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) (through reference chain: org.springframework.http.ResponseEntity["body"]->org.springframework.core.io.InputStreamResource["inputStream"]->java.io.FileInputStream["fd"])

Add the following configuration:

static {
    
    
	mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
}

The error disappears, but the exported Excel is empty! ? ?

It is worth noting that:
when using EasyExcel version 2.2.11, based on the above EasyExcelexport tool class, there is no problem, and Excel files can be exported with data.

After upgrading to 3.0.1a version, the export file is empty.

Export Excel file is empty

the problems mentioned above. I really can't figure it out, so raise an issue in the GitHub Issue that the export Excel is empty .

Workaround:
remove this line:writeSheet.setSheetName(fileName);

Me: Confused? ? ?

reference

Guess you like

Origin blog.csdn.net/lonelymanontheway/article/details/131258245