SpringBoot集成文件 - 集成EasyExcel之Excel导入导出

arcstack约 1480 字大约 5 分钟

SpringBoot集成文件 - 集成EasyExcel之Excel导入导出

EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。它能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。它是基于POI来封装实现的,主要解决其易用性,封装性和性能问题。本文主要介绍通过SpringBoot集成Excel实现Excel的导入,导出和填充模板等功能。@pdai

知识准备

需要了解EasyExcel,以及这个工具设计的初衷(为什么有了POI,还会需要EasyExcel?)。

什么是EasyExcel

EasyExcel是阿里开源的基于POI封装的Excel处理工具,更多请参考官方文档 在新窗口打开open in new window

EasyExcel是一个基于Java的、快速、简洁、解决大文件内存溢出的Excel处理工具。它能让你在不用考虑性能、内存的等因素的情况下,快速完成Excel的读、写等功能。

EasyExcel要解决POI什么问题?

因为EasyExcel是基于POI封装的,主要考虑的是易用性,封装性和性能问题。

Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,一个3M的excel用POI sax解析依然需要100M左右内存,改用easyexcel可以降低到几M,并且再大的excel也不会出现内存溢出;03版依赖POI的sax模式,在上层做了模型转换的封装,让使用者更加简单方便。

实现案例

这里展示SpringBoot集成EasyExcel导出用户列表的和导入用户列表的例子。

Pom依赖

引入poi的依赖包

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

导出Excel

User类

    package tech.pdai.springboot.file.excel.easyexcel.entity;

    import com.alibaba.excel.annotation.ExcelProperty;
    import lombok.AllArgsConstructor;
    import lombok.Builder;
    import lombok.Data;
    import lombok.NoArgsConstructor;

    /** * @author pdai */
    @Builder
    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User implements BaseEntity {

        /** * user id. */
        @ExcelProperty("ID")
        private Long id;

        /** * username. */
        @ExcelProperty("Name")
        private String userName;

        /** * email. */
        @ExcelProperty("Email")
        private String email;

        /** * phoneNumber. */
        @ExcelProperty("Phone")
        private long phoneNumber;

        /** * description. */
        @ExcelProperty("Description")
        private String description;

    }

UserController中导出的方法

    @ApiOperation("Download Excel")
    @GetMapping("/excel/download")
    public void download(HttpServletResponse response) {
        try {
            response.reset();
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Content-disposition",
                    "attachment;filename=user_excel_" + System.currentTimeMillis() + ".xlsx");
            userService.downloadExcel(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

UserServiceImple中导出Excel的主方法(是不是很简洁)

    @Override
    public void downloadExcel(ServletOutputStream outputStream) {
        EasyExcelFactory.write(outputStream, User.class).sheet("User").doWrite(this::getUserList);
    }
    private List<User> getUserList() {
        return Collections.singletonList(User.builder()
                .id(1L).userName("pdai").email("pdai@pdai.tech").phoneNumber(121231231231L)
                .description("hello world")
                .build());
    }

导出后的excel如下

springboot-file-excel-easyexcel-1.png
springboot-file-excel-easyexcel-1.png

导入Excel

我们将上面导出的excel文件导入。

UserController中导入的方法

    @ApiOperation("Upload Excel")
    @PostMapping("/excel/upload")
    public ResponseResult<String> upload(@RequestParam(value = "file", required = true) MultipartFile file) {
        try {
            userService.upload(file.getInputStream());
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.fail(e.getMessage());
        }
        return ResponseResult.success();
    }

UserServiceImple中导入Excel的主方法

    @Override
    public void upload(InputStream inputStream) throws IOException {
        // ReadListener不是必须的,它主要的设计是读取excel数据的后置处理(并考虑一次性读取到内存潜在的内存泄漏问题)
        EasyExcelFactory.read(inputStream, User.class, new ReadListener<User>() {

            @Override
            public void invoke(User user, AnalysisContext analysisContext) {
                cachedDataList.add(user);
            }

            @Override
            public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                cachedDataList.forEach(user -> log.info(user.toString()));
            }
        }).sheet().doRead();
    }

通过PostMan进行接口测试

springboot-file-excel-easyexcel-2.png 这里注意下,需要有字体的支持,比如如果没有字体支撑将会报如下告警:

    Warning: the font "Times" is not available, so "Lucida Bright" has been substituted, but may have unexpected appearance or behavor. Re-enable the "Times" font to remove this warning.

填充Excel模板

我们先来准备一个excel模板,考虑了横向表和纵向列表,以及单一信息等,基本上能满足多数的应用场景。

springboot-file-excel-easyexcel-4.png UserController中下载填充后的Excel方法

    @ApiOperation("Fill Excel Template")
    @GetMapping("/excel/fill")
    public void fillTemplate(HttpServletResponse response) {
        try {
            response.reset();
            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Content-disposition",
                    "attachment;filename=user_excel_template_" + System.currentTimeMillis() + ".xlsx");
            userService.fillExcelTemplate(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

UserServiceImpl中填充excel模板的方法

    // 模板注意 用{} 来表示你要用的变量 如果本来就有"{","}" 特殊字符 用"\{","\}"代替
    // {} 代表普通变量 {.} 代表是list的变量 {前缀.} 前缀可以区分不同的list
    @Override
    public void fillExcelTemplate(ServletOutputStream outputStream) {

        // 确保文件可访问,这个例子的excel模板,放在根目录下面
        String templateFileName = "/Users/pdai/Downloads/user_excel_template.xlsx";

        // 方案1
        try (ExcelWriter excelWriter = EasyExcelFactory.write(outputStream).withTemplate(templateFileName).build()) {
            WriteSheet writeSheet = EasyExcelFactory.writerSheet().build();
            FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
            // 如果有多个list 模板上必须有{前缀.} 这里的前缀就是 userList,然后多个list必须用 FillWrapper包裹
            excelWriter.fill(new FillWrapper("userList", getUserList()), fillConfig, writeSheet);
            excelWriter.fill(new FillWrapper("userList", getUserList()), fillConfig, writeSheet);

            excelWriter.fill(new FillWrapper("userList2", getUserList()), writeSheet);
            excelWriter.fill(new FillWrapper("userList2", getUserList()), writeSheet);

            Map<String, Object> map = new HashMap<>();
            map.put("user", "pdai");
            map.put("date", new Date());

            excelWriter.fill(map, writeSheet);
        }
    }

访问http://localhost:8080/user/excel/fill 下载

springboot-file-excel-easyexcel-3.png
springboot-file-excel-easyexcel-3.png

示例源码

https://github.com/realpdai/tech-pdai-spring-demos

参考文章

https://easyexcel.opensource.alibaba.com/docs/current/

上次编辑于:
贡献者: javatodo