利用阿里研发的easyexcel导入导出excel,避免OOM,并对excel加密保护

利用阿里研发的easyexcel导入导出excel,避免OOM,并对excel加密保护

  • 虽然POI是目前使用最多的用来做excel解析的框架,但这个框架并不那么完美。大部分使用POI都是使用他的userModel模式。userModel的好处是上手容易使用简单,随便拷贝个代码跑一下,剩下就是写业务转换了,虽然转换也要写上百行代码,相对比较好理解。然而userModel模式最大的问题是在于非常大的内存消耗,一个几兆的文件解析要用掉上百兆的内存。现在很多应用采用这种模式,之所以还正常在跑一定是并发不大,并发上来后一定会OOM或者频繁的full gc。
  • easyexcel核心原理
    写有大量数据的xlsx文件时,POI为我们提供了SXSSFWorkBook类来处理,这个类的处理机制是当内存中的数据条数达到一个极限数量的时候就flush这部分数据,再依次处理余下的数据,这个在大多数场景能够满足需求。
    读有大量数据的文件时,使用WorkBook处理就不行了,因为POI对文件是先将文件中的cell读入内存,生成一个树的结构(针对Excel中的每个sheet,使用TreeMap存储sheet中的行)。如果数据量比较大,则同样会产生java.lang.OutOfMemoryError: Java heap space错误。POI官方推荐使用“XSSF and SAX(event API)”方式来解决。
    分析清楚POI后要解决OOM有3个关键。
    1、1、文件解压文件读取通过文件形式
    在这里插入图片描述
    2、避免将全部全部数据一次加载到内存
    采用sax模式一行一行解析,并将一行的解析结果以观察者的模式通知处理。
    在这里插入图片描述
    3、抛弃不重要的数据
    Excel解析时候会包含样式,字体,宽度等数据,但这些数据是我们不关心的,如果将这部分数据抛弃可以大大降低内存使用。Excel中数据如下Style占了相当大的空间。
  • maven依赖
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>2.1.4</version>
</dependency>
  • 导出excel
    1、定义导出对象类
package com.jianmin.config;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import lombok.Data;

import java.util.Date;

/**
 * @Author: jianmin.li
 * @Description: 定义学生实体类
 * @Date: 2019/8/29 16:24
 * @Version: 1.0
 */
@Data
@ColumnWidth(20)
public class Student {

    /**
     * 将姓名指定为excel的第一列,表头为value
     */
    @ExcelProperty(index = 0, value = "姓名")
    private String name;
    /**
     * 将年龄指定为excel的第二列,表头为value
     */
    @ExcelProperty(index = 1, value = "年龄")
    private Integer age;
    /**
     * 将生日指定为第三列,表头为value,日期格式指定为'dd/MM/yyyy HH:mm:ss'
     */
    @ExcelProperty(index = 2, value = "生日")
    @DateTimeFormat("dd/MM/yyyy HH:mm:ss")
    private Date birthday;

    @Override
    public String toString() {
        return JSON.toJSONString(this,SerializerFeature.WriteDateUseDateFormat);
    }
}

2、导出excel代码示例

@GetMapping("/export")
    public void export(HttpServletResponse response) throws IOException {
        //设置响应域
        response.setContentType("application/octet-stream");
        response.setCharacterEncoding("utf-8");
        response.setHeader("Content-disposition","attachment;filename=demo.xlsx");
        //单元格合并策略-->对第一列每两行进行一次合并
        LoopMergeStrategy loopMergeStrategy = new LoopMergeStrategy(2,0);
        //单元格合并策略-->对第二列每四行进行一次合并
        LoopMergeStrategy loopMergeStrategy1 = new LoopMergeStrategy(4,1);
        //单元格合并策略-->对第一列到第二列的第一行到第二行进行合并,所有合并策略均不合并表头
        OnceAbsoluteMergeStrategy onceAbsoluteMergeStrategy = new OnceAbsoluteMergeStrategy(1,2,0,1);
        //构建excelWriter
        @Cleanup("finish") ExcelWriter writer = EasyExcel
                //将数据映射到Student实体类并响应到浏览器
                .write(new BufferedOutputStream(response.getOutputStream()),Student.class)
                //自动关闭输出流
                .autoCloseStream(Boolean.TRUE)
                //excel版本,强烈建议导出07版excel,因为省内存
                .excelType(ExcelTypeEnum.XLSX)
                //是否需要表头
                .needHead(Boolean.TRUE)
                //注册单元格合并策略
                //.registerWriteHandler(loopMergeStrategy)
               //.registerWriteHandler(loopMergeStrategy1)
                .registerWriteHandler(onceAbsoluteMergeStrategy)
                //设置excel保护密码
                .password("123456")
                //构建
                .build();
        //初始化sheet
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
        writeSheet.setSheetNo(1);
        writeSheet.setSheetName("Sheet1");
        //获取数据
        List<Student> list = getStudents();
        //导出excel,注意导出结束要finish
        writer.write(list,writeSheet);
    }

    private List<Student> getStudents() {
        List<Student> list = new ArrayList<>();
        Student student = new Student();
        Student student1 = new Student();
        Student student2 = new Student();
        Student student3 = new Student();
        list.add(student);
        list.add(student1);
        list.add(student2);
        list.add(student3);
        student.setAge(25);
        student.setBirthday(new Date());
        student.setName("王老五");
        student1.setAge(26);
        student1.setBirthday(new Date());
        student1.setName("王老五");
        student2.setAge(27);
        student2.setBirthday(new Date());
        student2.setName("李老六");
        student3.setAge(28);
        student3.setBirthday(new Date());
        student3.setName("李老六");
        return list;
    }

3、导出结果

导出excel

  • 导入excel
    1、自定义分析事件监听
package com.jianmin.config;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSON;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @Author: jianmin.li
 * @Description: 自定义分析事件监听
 * @Date: 2019/8/29 16:29
 * @Version: 1.0
 */
public class ExcelListener extends AnalysisEventListener<Student> {
    private final List<Student> list = new ArrayList<>();

    /**
     * 一行一行的读取数据,将数据放到集合,批量处理
     *
     * @param student
     * @param analysisContext
     * @return : void
     * @Author: jianmin.li
     * @Date: 2019/10/23 9:59
     */
    @Override
    public void invoke(Student student,AnalysisContext analysisContext) {
        list.add(student);
        if (list.size() == 2) {
            doSomething();
        }
    }

    private void doSomething() {
        for (Student student : list) {
            System.err.println(student);
        }
        list.clear();
    }

    /**
     * 数据读取完进行的操作
     *
     * @param analysisContext
     * @return : void
     * @Author: jianmin.li
     * @Date: 2019/10/23 10:05
     */
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        if (list.size() > 0) {
            doSomething();
        }
    }

    /**
     * 获取表头信息
     *
     * @param headMap
     * @param context
     * @return : void
     * @Author: jianmin.li
     * @Date: 2019/10/23 10:06
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap,AnalysisContext context) {
        System.err.println(JSON.toJSONString(headMap));
        Integer sheetNo = context.readSheetHolder().getReadSheet().getSheetNo();
        System.err.println(sheetNo);
    }
}


2、excel导入示例

 @PostMapping("/upload")
    public void upload(MultipartFile file) throws IOException {
        EasyExcel
                //将数据映射到Student实体类,并由自定义的分析事件监听处理数据
                .read(new BufferedInputStream(file.getInputStream()),Student.class,new ExcelListener())
                //表头最大行数
                .headRowNumber(1)
                //是否对单元格内容自动去除两边的空格
                .autoTrim(Boolean.TRUE)
                //是否自动关闭输入流
                .autoCloseStream(Boolean.TRUE)
                //excel保护密码
                .password("123456")
                //开始导入
                .doReadAll();
    }

3、导入结果

excel导入

  • 2
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
好的,我可以为您提供一些关于使用EasyExcel导入导出财报Excel的指导。 首先,您需要在您的项目中引入EasyExcel的依赖: ```xml <dependency> <groupId>com.alibaba</groupId> <artifactId>easyexcel</artifactId> <version>2.2.11</version> </dependency> ``` 接下来,您可以使用EasyExcel提供的注解来定义您的Excel模板: ```java @Data public class FinancialReport { @ExcelProperty("日期") private String date; @ExcelProperty("营业收入") private Double revenue; @ExcelProperty("净利润") private Double netProfit; // ... 其他属性 } ``` 在导出Excel时,您可以使用以下代码创建一个EasyExcel的写出器,并将数据写入Excel中: ```java public void export(List<FinancialReport> data, String filePath) throws IOException { try (OutputStream out = new FileOutputStream(filePath)) { ExcelWriter writer = EasyExcelFactory.getWriter(out); Sheet sheet = new Sheet(1, 0, FinancialReport.class); sheet.setSheetName("财报"); writer.write(data, sheet); writer.finish(); } } ``` 在导入Excel时,您可以使用以下代码创建一个EasyExcel的读取器,并将Excel中的数据读取到一个List中: ```java public List<FinancialReport> import(String filePath) throws IOException { try (InputStream in = new FileInputStream(filePath)) { ExcelReader reader = EasyExcelFactory.getReader(in); List<Sheet> sheets = reader.getSheets(); if (sheets.isEmpty()) { return Collections.emptyList(); } List<FinancialReport> data = new ArrayList<>(); reader.read(sheets.get(0), FinancialReport.class, new AnalysisEventListener<FinancialReport>() { @Override public void invoke(FinancialReport report, AnalysisContext context) { data.add(report); } @Override public void doAfterAllAnalysed(AnalysisContext context) {} }); return data; } } ``` 以上就是使用EasyExcel导入导出财报Excel的简单示例。当然,如果您需要更加复杂的操作,还可以参考EasyExcel的官方文档进行更深入的学习。
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值