简介:EasyExcel是一个基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。
官方文档EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel

前提准备

依赖

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

Excel导入

最简单的读步骤:

  1. 创建excel对应的实体对象
  2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器(StringExcelListener)
  3. 直接读即可
    测试实体类
@Data
@EqualsAndHashCode
public class DemoData {
    private String string;
    private Date date;
    private Double doubleData;
}

StringExcelListener监听类
有个很重要的点,StringExcelListener不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去。

@Slf4j
public class StringExcelListener extends AnalysisEventListener
{
    /**
     * 自定义用于暂时存储data
     * 可以通过实例获取该值
     */
    private List<List<String>> datas = ListUtils.newArrayList();

    /**
     * 每解析一行都会回调invoke()方法
     * @param o
     * @param analysisContext
     */
    @Override
    public void invoke(Object o, AnalysisContext analysisContext)
    {
        @SuppressWarnings("unchecked") Map<String, String> stringMap = (HashMap<String, String>) o;
        //数据存储到list,供批量处理,或后续自己业务逻辑处理。
        datas.add(new ArrayList<>(stringMap.values()));
        //根据自己业务做处理
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext)
    {
        log.info("所有数据解析完成!");
    }

    /**
     * 返回数据
     *
     * @return 返回读取的数据集合
     **/
    public List<List<String>> getDatas() {
        return datas;
    }

    /**
     * 设置读取的数据集合
     *
     * @param datas 设置读取的数据集合
     **/
    public void setDatas(List<List<String>> datas) {
        this.datas = datas;
    }
}

封装ExcelUtils工具类

@Component
public class ExcelUtils
{
    /**
     * 读取excel数据
     *
     * @param file
     * @return result
     * @author hecc
     * @date 2022/9/27 17:43
     **/
    public List<List<String>> writeWithoutHead(MultipartFile file) {
        List<List<String>> result = Lists.newArrayList();
        try {
            result = writeWithoutHead(file.getInputStream());
        } catch (IOException e) {
            throw new RuntimeException("APP_ADD_ERROR,文件解析异常");
        }
        if (CollectionUtils.isEmpty(result)) {
            throw new RuntimeException("APP_ADD_ERROR,文件为空");
        }
        return result;
    }

    public List<List<String>> writeWithoutHead(InputStream inputStream) {
        StringExcelListener listener = new StringExcelListener();
        ExcelReader excelReader = EasyExcelFactory.read(inputStream, null, listener).headRowNumber(1).build();
        excelReader.read();
        List<List<String>> datas = listener.getDatas();
        excelReader.finish();
        return datas;
    }
}

controller层

@RestController
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE,makeFinal = true)
@RequestMapping("/excel")
public class ExcelController
{
    ExcelUtils excelUtils;

    @PostMapping("/read")
    public void read(@RequestParam("file") MultipartFile file) {
        if(file.isEmpty()){
            throw new RuntimeException("请上传文件!");
        }
        List<List<String>> lists = excelUtils.writeWithoutHead(file);
        //处理,存入对象
        List<DemoData> demoDataList = ListUtils.newArrayList();
        lists.forEach(item->{
            DemoData demoData = new DemoData();
            demoData.setString(item.get(0));
            try
            {
                demoData.setDate(DateUtils.parseDate(item.get(1)));
            }
            catch (ParseException e)
            {
                throw new RuntimeException(e);
            }
            demoData.setDoubleData(Double.valueOf(item.get(2)));
            demoDataList.add(demoData);
        });
        demoDataList.forEach(System.out::println);
    }
}

运行结果

修改版本

测试实体类

@Data
@EqualsAndHashCode
public class DemoData {
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    /**
     * 这里用string 去接日期才能格式化。其它的也可能需要String才行
     */
    @DateTimeFormat("yyyy年MM月dd日")
    private String date;
    @ExcelProperty(index = 2)
    private Double doubleData;
}

StringExcelListener监听类
有个很重要的点,StringExcelListener不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去。

@Slf4j
public class StringExcelListener<T> extends AnalysisEventListener<T>
{
    /**
     * 自定义用于暂时存储data
     * 可以通过实例获取该值
     */
    private List<T> datas = ListUtils.newArrayList();

    /**
     * 每解析一行都会回调invoke()方法
     * @param o
     * @param analysisContext
     */
    @Override
    public void invoke(T o, AnalysisContext analysisContext)
    {
        //@SuppressWarnings("unchecked") Map<String, String> stringMap = (HashMap<String, String>) o;
        //数据存储到list,供批量处理,或后续自己业务逻辑处理。
        datas.add(o);
        //根据自己业务做处理
    }

    /**
     * 所有数据解析完成了 都会来调用
     *
     * @param analysisContext
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext)
    {
        log.info("所有数据解析完成!");
    }

    /**
     * 返回数据
     *
     * @return 返回读取的数据集合
     **/
    public List<T> getDatas() {
        return datas;
    }

    /**
     * 设置读取的数据集合
     *
     * @param datas 设置读取的数据集合
     **/
    public void setDatas(List<T> datas) {
        this.datas = datas;
    }
}

封装ExcelUtils工具类

@Component
public class ExcelUtils
{
    /**
     * 读取excel数据
     *
     * @param file
     * @return result
     * @date 2022/9/27 17:43
     **/
    public <T> List<T> writeWithoutHead(MultipartFile file, Class<T> clz ) {
        List<T> result = Lists.newArrayList();
        try {
            result = writeWithoutHead(file.getInputStream(),clz);
        } catch (IOException e) {
            throw new RuntimeException("APP_ADD_ERROR,文件解析异常");
        }
        if (CollectionUtils.isEmpty(result)) {
            throw new RuntimeException("APP_ADD_ERROR,文件为空");
        }
        return result;
    }

    public <T> List<T> writeWithoutHead(InputStream inputStream,Class<T> clz) {
        StringExcelListener listener = new StringExcelListener();
        ExcelReader excelReader = EasyExcelFactory.read(inputStream, clz, listener).headRowNumber(1).build();
        excelReader.read();
        List<T> datas = listener.getDatas();
        excelReader.finish();
        return datas;
    }

}

controller层

@RestController
@RequiredArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE,makeFinal = true)
@RequestMapping("/excel")
public class ExcelController
{
    ExcelUtils excelUtils;

    @PostMapping("/read")
    public void read(@RequestParam("file") MultipartFile file) {
        if(file.isEmpty()){
            throw new RuntimeException("请上传文件!");
        }
        List<DemoData> demoDataList = excelUtils.writeWithoutHead(file,DemoData.class);
        demoDataList.forEach(System.out::println);
    }
}

运行结果

Excel导出

1.简单导出

测试实体类

@Data
@EqualsAndHashCode
public class DemoData {
    @ExcelProperty("字符串标题")
    private String string;
    @ExcelProperty("日期标题")
    @DateTimeFormat("yyyy年MM月dd日")
    private Date date;
    @ExcelProperty("数字标题")
    private Double doubleData;
    /**
     * 忽略这个字段
     */
    @ExcelIgnore
    private String ignore;
}

controller层

 @PostMapping("/write")
    public void write() {
        //写法1
        String fileName ="simpleWrite" + System.currentTimeMillis() + ".xlsx";
        EasyExcel.write(fileName, DemoData.class)
            .sheet("模板")
            .doWrite(() -> {
                // 分页查询数据
                return data();
            });
        // 写法2
        fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
        // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
        // 如果这里想使用03 则 传入excelType参数即可
        EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());

        // 写法3
        fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
        // 这里 需要指定写用哪个class去写
        try (ExcelWriter excelWriter = EasyExcel.write(fileName, DemoData.class).build()) {
            WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
            excelWriter.write(data(), writeSheet);
        }
    }

    //测试数据
    private List<DemoData> data() {
        List<DemoData> list = ListUtils.newArrayList();
        for (int i = 0; i < 10; i++) {
            DemoData data = new DemoData();
            data.setString("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }

运行结果:
项目目录下:
 

2.web导出

实体类同上
controller层

    /**
     * 文件下载(失败了会返回一个有部分数据的Excel)
     * <p>
     * 1. 创建excel对应的实体对象 参照{@link DemoData}
     * <p>
     * 2. 设置返回的 参数
     * <p>
     * 3. 直接写,这里注意,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(), DemoData.class).sheet("模板").doWrite(data());
    }

  //测试数据
    private List<DemoData> data() {
        List<DemoData> list = ListUtils.newArrayList();
        for (int i = 0; i < 10; i++) {
            DemoData data = new DemoData();
            data.setString("字符串" + i);
            data.setDate(new Date());
            data.setDoubleData(0.56);
            list.add(data);
        }
        return list;
    }

参考:阿里的Easyexcel读取Excel文件(最新版本)_easyexcel 读取excel_要争气的博客-CSDN博客

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐