EasyExcel uses entity classes for read and write operations

1. Overview of EasyExcel

1.1 Basic functions of EasyExcel

  • Data import: reduce input workload

  • Data Export: Statistics Archive

  • Data transmission: data transmission between heterogeneous systems

1.2 Problems with other parsing frameworks

JavaDomain analysis and generation Excelof well-known frameworks include Apache poi, jxletc.

But they all have a serious problem that consumes a lot of memory.

If the system has a small amount of concurrency, it may be okay, but once the concurrency is up, it will definitely appear OOMor JVMbe frequent full gc.

1.3 Advantages of EasyExcel

EasyExcelexceIt is an l processing framework open sourced by Alibaba . It is famous for its simple use and memory saving .

EasyExcelThe main reason for greatly reducing the memory usage is that Excelthe file data is not loaded into the memory all at once during parsing.

Instead, it reads data line by line from the disk and parses them one by one.

EasyExcelAdopt a line-by-line parsing mode, and notify the processing ( AnalysisEventListener) of the line-by-line parsing result in the observer mode.

In summary:

EasyExcelis an open source project based on Javasimple and memory-efficient read and write .Excel

MIt supports reading and writing hundreds of files while saving memory as much as possible Excel.

1.4 Official document address

https://alibaba-easyexcel.github.io/index.html

1.5 github address

https://github.com/alibaba/easyexcel

2. Introducing dependencies

<dependencies>
    <!-- 查看excel的maven仓库 https://mvnrepository.com/artifact/com.alibaba/easyexcel -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>easyexcel</artifactId>
        <version>3.1.2</version>
    </dependency>
</dependencies>

3. Write operation of EasyExcel

3.1 Create entity classes

// lombok注解,自动生成getter/setter、构造方法、toString等实体类通用方法
@Data
public class Student {
    
    
    // @ExcelProperty注解 设置当前字段表头名称和所在的列的索引,index为0,代表sno字段会被插入到第一列
    @ExcelProperty(value = "学生编号",index = 0)
    private int sno;
    @ExcelProperty(value = "学生姓名",index = 1)
    private String sname;
    // @ExcelIgnore 导出时忽略这个字段
    @ExcelIgnore
    private String ignore;
}

3.2 Writing methods to generate data

//循环设置要添加的数据,最终封装到list集合中
private static List<Student> data() {
    
    
    List<Student> list = new ArrayList<>();
    for (int i = 0; i < 10; i++) {
    
    
        Student data = new Student();
        data.setSno(i);
        data.setSname("张三"+i);
        list.add(data);
    }
    return list;
}

3.3 Write data to a single sheet file in Excel

Writing method 1 (common api):

public static void main(String[] args) throws Exception {
    
    
    // 指定文件位置
    String fileName = "D:\\test.xlsx";
    // 指定各个参数并向指定位置写出一个Excel文件
    EasyExcel.write(fileName, Student.class).sheet(0, "学生列表").doWrite(data());
    
}

Writing method 2 (lambda expression):

public static void main(String[] args) throws Exception {
    
    
    // 指定文件位置
    String fileName = "D:\\test.xlsx";
    EasyExcel.write(fileName, Student.class).sheet("学生列表")
             .doWrite(() -> {
    
    
                 // 这个函数中返回需要的数据即可
                 return data();
             });
}

Detailed explanation of the parameters passed:

  • writeThe method needs to specify the location of the file and which class to use for writing . The default location for writing data is the firstsheet

  • sheetThe method needs to specify the name of the one currently writing datasheet (required) and the number sheetto write data to (not required)

  • doWriteThe method can receive a collection of data to be written or pass in an lambdaexpression, and the return value of the expression is still a collection of data

3.4 Write data to multiple sheet files in Excel

public static void main(String[] args) throws Exception {
    
    
    // 指定文件位置
    String fileName = "D:\\test.xlsx";
    // 创建Excel写出对象
    ExcelWriter excelWriter = EasyExcel.write(fileName, Student.class).build();
    // 创建写出的Excel文件的sheet对象   并指定sheet的位置和名字
    WriteSheet sheet1 = EasyExcel.writerSheet(0, "学生列表1").build();
    WriteSheet sheet2 = EasyExcel.writerSheet(1, "学生列表2").build();
    // 向sheet中写入数据
    excelWriter.write(data(), sheet1);
    excelWriter.write(data(), sheet2);
    
    //关闭流对象(这是和上面写法的区别,上面的写法会自动关,这里需要手动关)
    excelWriter.finish();
}

4. Write operation of EasyExcel in web application (download)

In webthe application, the operation of export (download) is often encountered Excel.

Mainly EasyExceluse HttpServletResponsethe object to get the output stream to write out the file to the browser.

import org.springframework.web.multipart.MultipartFile;

/**
 * 在web应用中读写案例
 **/
@Controller
public class WebTest {
    
    

    @Autowired
    private UploadDAO uploadDAO;

    /**
     * 文件下载(失败了会返回一个有部分数据的Excel)
     * 1. 创建excel对应的实体对象
     * 2. 设置返回的 参数
     * 3. 直接写出Excel
     * 		这里注意,doWrite执行完后会自动关闭流, 主动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(), Student.class)
            	 .sheet("sheet的名称").doWrite(data());
    }

    /**
     * 文件下载并且失败的时候返回json(默认失败了会返回一个有部分数据的Excel)
     * @since 2.1.1
     */
    @GetMapping("downloadFailedUsingJson")
    public void downloadFailedUsingJson(HttpServletResponse response) throws IOException {
    
    
        try {
    
    
            response.setContentType(
                "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            response.setCharacterEncoding("utf-8");
            // URLEncoder.encode可以防止中文乱码
            String fileName = URLEncoder.encode("测试", "UTF-8").replaceAll("\\+", "%20");
            response.setHeader("Content-disposition", 
                               "attachment;filename*=utf-8''" + fileName + ".xlsx");
            // 这里需要设置不关闭流 autoCloseStream(Boolean.FALSE)
            EasyExcel.write(response.getOutputStream(), Student.class)
                	 .autoCloseStream(Boolean.FALSE)
                	 .sheet("模板")
                	 .doWrite(data());
        } catch (Exception e) {
    
    
            // 重置response
            response.reset();
            response.setContentType("application/json");
            response.setCharacterEncoding("utf-8");
            Map<String, String> map = MapUtils.newHashMap();
            map.put("status", "failure");
            map.put("message", "下载文件失败" + e.getMessage());
            response.getWriter().println(JSON.toJSONString(map));
        }
    }

    //循环设置要添加的数据,最终封装到list集合中
    private static List<Student> data() {
    
    
        List<Student> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
    
    
            Student data = new Student();
            data.setSno(i);
            data.setSname("张三"+i);
            list.add(data);
        }
        return list;
    }
}

Five, Excel read operation

5.1 Create entity class

// lombok注解,自动生成getter/setter、构造方法、toString等实体类通用方法
@Data
public class Student {
    
    
    // @ExcelProperty注解 设置当前字段表头名称和所在的列的索引,index为0,代表sno字段会被插入到第一列
    @ExcelProperty(value = "学生编号",index = 0)
    private int sno;
    @ExcelProperty(value = "学生姓名",index = 1)
    private String sname;
    // @ExcelIgnore 导出时忽略这个字段
    @ExcelIgnore
    private String ignore;
}

5.2 Create a listener for read operations

1) Basic version

public class ExcelListener extends AnalysisEventListener<Student> {
    
    
    
    //创建list集合封装最终的数据
    List<Student> list = new ArrayList<>();
    
    //读取excel表头信息时执行
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
    
    
        System.out.println("表头信息:"+headMap);
    }
    
    // 读取excel内容信息时执行
    // EasyExcel会会一行一行去读取excle内容,每解析excel文件中的一行数据,都会调用一次invoke方法
    @Override
    public void invoke(Student stu, AnalysisContext analysisContext) {
    
    
        System.out.println("***" + stu);
        list.add(user);
    }
   
    
    //读取完成后执行
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    
    
    }
}

Notice:

Custom listener classes cannot be springmanaged, requiring a new object excelfor each read.new

If springthe managed object is used in it, you can use the constructor to pass it in:

// 如果用到了就在监听器中加上类似代码,去使用dao层或者service层中的逻辑把读取到的数据写到数据库中
private DemoDAO demoDAO;

public DemoDataListener(DemoDAO demoDAO) {
    
    
    this.demoDAO = demoDAO;
}

2) When you need to use the logic of saving data in spring

@Slf4j
public class ExcelListener extends AnalysisEventListener<Student> {
    
    
    
    //创建list集合封装从Excel文件中读取的数据
    List<Student> list = new ArrayList<>();
    
    // list中每达到10条数据就存储数据库,然后清理list ,方便内存回收
    // 实际使用中可以根据服务器性能设置更多条
    private static final int BATCH_COUNT = 10;
     
    // 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。
    // 如果不用存储从Excel文件中读取的数据,那么这个对象就没用
    private DemoDAO demoDAO;
    
    // 无参构造
    public DemoDataListener() {
    
    
    }
    
    // 有参构造 可以在每次创建Listener对象的时候需要把spring管理的类传进来
    public DemoDataListener(DemoDAO demoDAO) {
    
    
        this.demoDAO = demoDAO;
    }
    
    //读取excel表头信息时执行
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
    
    
        System.out.println("表头信息:"+headMap);
    }
    
    // 读取excel内容信息时执行
    // EasyExcel会会一行一行去读取excle内容,每解析excel文件中的一行数据,都会调用一次invoke方法
    @Override
    public void invoke(Student stu, AnalysisContext analysisContext) {
    
    
        System.out.println("***" + stu);
        list.add(user);
         // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_COUNT) {
    
    
            saveData();
            // 存储完成清理 list
            list = new ArrayList<Student>(BATCH_COUNT);
        }
    }
   
    //读取完成后执行
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
    
    
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        list = new ArrayList<Student>(BATCH_COUNT);
    }
    
     // 把数据存储到数据库中
    private void saveData() {
    
    
        log.info(BATCH_COUNT + "条数据,开始存储数据库!", cachedDataList.size());
        demoDAO.save(list);
        log.info("存储数据库成功!");
    }
}

5.3 Read the data of a single sheet in the Excel file

public class EasyExcelReadDemo {
    
    

    public static void main(String[] args) {
    
    
        // 指定要读取文件的位置
    	String fileName = "D:\\test.xlsx";
        // read方法指定文件名名称、使用哪个实体类解析、使用哪个监听器类处理
        // sheet方法指定读取哪个sheet的数据
        // doRead() 方法发起最终的读取操作
        EasyExcel.read(fileName, Student.class, new StudentListener()).sheet(0).doRead();
    }
}

5.4 Read the data of multiple sheets of the excel file

public class EasyExcelReadDemo {
    
    

    public static void main(String[] args) {
    
    
        // 指定要读取文件的位置
    	String fileName = "D:\\test.xlsx";
        // 创建Excel读对象,需要指定读取哪个Excel
        ExcelReader excelReader = EasyExcel.read(fileName).build();
        // 创建需要读取的Excel中的sheet对象
        ReadSheet sheet1 = EasyExcel.readSheet(0)
                                    .head(Student.class)
                                    .registerReadListener(new StudentListener()).build();
        ReadSheet sheet2 = EasyExcel.readSheet(1)
                                    .head(Student.class)
                                    .registerReadListener(new StudentListener()).build();
		// 批量读取sheet1对象和sheet2对象中的数据
        excelReader.read(sheet1, sheet2);
		// 关闭流资源
        excelReader.finish();
    }
}

6. Read operation (upload) of EasyExcel in web application

import org.springframework.web.multipart.MultipartFile;

/**
 * 在web应用中读写案例
 **/
@Controller
public class WebTest {
    
    

    @Autowired
    private DemoDAO demoDAO;
0
    /**
     * 文件上传
     * 1. 创建excel对应的实体对象 参照{@link UploadData}
     * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器
     * 3. 直接读即可
     */
    @PostMapping("upload")
    @ResponseBody
    public String upload(MultipartFile file) throws IOException {
    
    
        EasyExcel.read(file.getInputStream(), 
                       Student.class, 
                       new UploadDataListener(demoDAO))
            	 .sheet()
            	 .doRead();
        return "success";
    }

    //循环设置要添加的数据,最终封装到list集合中
    private static List<Student> data() {
    
    
        List<Student> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
    
    
            Student data = new Student();
            data.setSno(i);
            data.setSname("张三"+i);
            list.add(data);
        }
        return list;
    }
}

Guess you like

Origin blog.csdn.net/qq_44749491/article/details/127860995