优雅的使用easyexcel一行代码导出和读取excel

俗话说的好,前人栽树后人乘凉。但是后人也不能忘了栽树人,

注明出处,向阿里开源大佬致敬 https://github.com/alibaba/easyexcel

JAVA解析Excel工具easyexcel

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

一. 依赖

首先是添加该项目的依赖,目前的版本是 1.1.2-beta4

<dependency>
	<groupId>com.alibaba</groupId>
	<artifactId>easyexcel</artifactId>
	<version>1.1.2-beta4</version>
</dependency>

 

二. 需要的类

1、捕获相关 Exception

public class ExcelException extends RuntimeException {
    public ExcelException(String message) {
        super(message);
}

2. ExcelListener

public class ExcelListener extends AnalysisEventListener {

    //自定义用于暂时存储data。
    //可以通过实例获取该值
    private List<Object> datas = new ArrayList<>();

    /**
     * 通过 AnalysisContext 对象还可以获取当前 sheet,当前行等数据
     */
    @Override
    public void invoke(Object object, AnalysisContext context) {
        //数据存储到list,供批量处理,或后续自己业务逻辑处理。
        datas.add(object);
        //根据业务自行 do something
        doSomething();

        /*
        如数据过大,可以进行定量分批处理
        if(datas.size()<=100){
            datas.add(object);
        }else {
            doSomething();
            datas = new ArrayList<Object>();
        }
         */

    }

    /**
     * 根据业务自行实现该方法
     */
    private void doSomething() {
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        /*
            datas.clear();
            解析结束销毁不用的资源
         */
    }

    public List<Object> getDatas() {
        return datas;
    }

    public void setDatas(List<Object> datas) {
        this.datas = datas;
    }

3. ExcelUtil

public class ExcelUtil {
    /**
     * 读取 Excel(多个 sheet)
     *
     * @param excel    文件
     * @param rowModel 实体类映射,继承 BaseRowModel 类
     * @return Excel 数据 list
     */
    public static List<Object> readExcel(MultipartFile excel, BaseRowModel rowModel) {
        ExcelListener excelListener = new ExcelListener();
        ExcelReader reader = getReader(excel, excelListener);
        if (reader == null) {
            return null;
        }
        for (Sheet sheet : reader.getSheets()) {
            if (rowModel != null) {
                sheet.setClazz(rowModel.getClass());
            }
            reader.read(sheet);
        }
        return excelListener.getDatas();
    }

    /**
     * 读取某个 sheet 的 Excel
     *
     * @param excel    文件
     * @param rowModel 实体类映射,继承 BaseRowModel 类
     * @param sheetNo  sheet 的序号 从1开始
     * @return Excel 数据 list
     */
    public static List<Object> readExcel(MultipartFile excel, BaseRowModel rowModel, int sheetNo) {
        return readExcel(excel, rowModel, sheetNo, 1);
    }

    /**
     * 读取某个 sheet 的 Excel
     *
     * @param excel       文件
     * @param rowModel    实体类映射,继承 BaseRowModel 类
     * @param sheetNo     sheet 的序号 从1开始
     * @param headLineNum 表头行数,默认为1
     * @return Excel 数据 list
     */
    public static List<Object> readExcel(MultipartFile excel, BaseRowModel rowModel, int sheetNo,
                                         int headLineNum) {
        ExcelListener excelListener = new ExcelListener();
        ExcelReader reader = getReader(excel, excelListener);
        if (reader == null) {
            return null;
        }
        reader.read(new Sheet(sheetNo, headLineNum, rowModel.getClass()));
        return excelListener.getDatas();
    }

    /**
     * 导出 Excel :一个 sheet,带表头
     *
     * @param response  HttpServletResponse
     * @param list      数据 list,每个元素为一个 BaseRowModel
     * @param fileName  导出的文件名
     * @param sheetName 导入文件的 sheet 名
     * @param object    映射实体类,Excel 模型
     */
    public static void writeExcel(HttpServletResponse response, List<? extends BaseRowModel> list,
                                  String fileName, String sheetName, BaseRowModel object) {
        ExcelWriter writer = new ExcelWriter(getOutputStream(fileName, response), ExcelTypeEnum.XLSX);
        Sheet sheet = new Sheet(1, 0, object.getClass());
        sheet.setSheetName(sheetName);
        writer.write(list, sheet);
        writer.finish();
    }

    /**
     * 导出 Excel :多个 sheet,带表头
     *
     * @param response  HttpServletResponse
     * @param list      数据 list,每个元素为一个 BaseRowModel
     * @param fileName  导出的文件名
     * @param sheetName 导入文件的 sheet 名
     * @param object    映射实体类,Excel 模型
     */
    public static ExcelWriterFactroy writeExcelWithSheets(HttpServletResponse response, List<? extends BaseRowModel> list,
                                                          String fileName, String sheetName, BaseRowModel object) {
        ExcelWriterFactroy writer = new ExcelWriterFactroy(getOutputStream(fileName, response), ExcelTypeEnum.XLSX);
        Sheet sheet = new Sheet(1, 0, object.getClass());
        sheet.setSheetName(sheetName);
        writer.write(list, sheet);
        return writer;
    }

    /**
     * 导出文件时为Writer生成OutputStream
     */
    private static OutputStream getOutputStream(String fileName, HttpServletResponse response) {
        //创建本地文件
        String filePath = fileName + ".xlsx";
        File dbfFile = new File(filePath);
        try {
            if (!dbfFile.exists() || dbfFile.isDirectory()) {
                dbfFile.createNewFile();
            }
            fileName = new String(filePath.getBytes(), "ISO-8859-1");
            response.addHeader("Content-Disposition", "filename=" + fileName);
            return response.getOutputStream();
        } catch (IOException e) {
            throw new ExcelException("创建文件失败!");
        }
    }

    /**
     * 返回 ExcelReader
     *
     * @param excel         需要解析的 Excel 文件
     * @param excelListener new ExcelListener()
     */
    private static ExcelReader getReader(MultipartFile excel,
                                         ExcelListener excelListener) {
        String filename = excel.getOriginalFilename();
        if (filename == null || (!filename.toLowerCase().endsWith(".xls") && !filename.toLowerCase().endsWith(".xlsx"))) {
            throw new ExcelException("文件格式错误!");
        }
        InputStream inputStream;
        try {
            inputStream = new BufferedInputStream(excel.getInputStream());
            return new ExcelReader(inputStream, null, excelListener, false);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
}

4. ExcelWriterFactroy

public class ExcelWriterFactroy extends ExcelWriter {
    private OutputStream outputStream;
    private int sheetNo = 1;

    public ExcelWriterFactroy(OutputStream outputStream, ExcelTypeEnum typeEnum) {
        super(outputStream, typeEnum);
        this.outputStream = outputStream;
    }

    public ExcelWriterFactroy write(List<? extends BaseRowModel> list, String sheetName,
                                    BaseRowModel object) {
        this.sheetNo++;
        try {
            Sheet sheet = new Sheet(sheetNo, 0, object.getClass());
            sheet.setSheetName(sheetName);
            this.write(list, sheet);
        } catch (Exception ex) {
            ex.printStackTrace();
            try {
                outputStream.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return this;
    }

    @Override
    public void finish() {
        super.finish();
        try {
            outputStream.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

三. 读取 Excel

读取 Excel 时只需要调用 ExcelUtil.readExcel() 方法

@RequestMapping(value = "readExcel", method = RequestMethod.POST)
public Object readExcel(MultipartFile excel) {
    return ExcelUtil.readExcel(excel, new ImportInfo());
}

其中 excel 是 MultipartFile 类型的文件对象,而 new ImportInfo() 是该 Excel 所映射的实体对象,需要继承 BaseRowModel类,如:

public class ImportInfo extends BaseRowModel {
    @ExcelProperty(index = 0)
    private String name;

    @ExcelProperty(index = 1)
    private String age;

    @ExcelProperty(index = 2)
    private String email;

    /*
        作为 excel 的模型映射,需要 setter 方法
     */
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getAge() {
        return age;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}

作为映射实体类,通过 @ExcelProperty 注解与 index 变量可以标注成员变量所映射的列,同时不可缺少 setter 方法

 

四. 导出 Excel

1. 导出的 Excel 只拥有一个 sheet

只需要调用 ExcelUtil.writeExcelWithSheets() 方法:

@RequestMapping(value = "writeExcel", method = RequestMethod.GET)
public void writeExcel(HttpServletResponse response) throws IOException {
    List<ExportInfo> list = getList();
    String fileName = "一个 Excel 文件";
    String sheetName = "第一个 sheet";

    ExcelUtil.writeExcelWithSheets(response, list, fileName, sheetName, new ExportInfo());
    }

fileName,sheetName 分别是导出文件的文件名和 sheet 名,new ExportInfo() 为导出数据的映射实体对象,list 为导出数据。

对于映射实体类,可以根据需要通过 @ExcelProperty 注解自定义表头,当然同样需要继承 BaseRowModel 类,如:

public class ExportInfo extends BaseRowModel {
    @ExcelProperty(value = "姓名" ,index = 0)
    private String name;

    @ExcelProperty(value = "年龄",index = 1)
    private String age;

    @ExcelProperty(value = "邮箱",index = 2)
    private String email;

    @ExcelProperty(value = "地址",index = 3)
    private String address;
}

value 为列名,index 为列的序号

如果需要复杂一点,可以实现如下图的效果: 

对应的实体类写法如下:

public class MultiLineHeadExcelModel extends BaseRowModel {

    @ExcelProperty(value = {"表头1","表头1","表头31"},index = 0)
    private String p1;

    @ExcelProperty(value = {"表头1","表头1","表头32"},index = 1)
    private String p2;

    @ExcelProperty(value = {"表头3","表头3","表头3"},index = 2)
    private int p3;

    @ExcelProperty(value = {"表头4","表头4","表头4"},index = 3)
    private long p4;

    @ExcelProperty(value = {"表头5","表头51","表头52"},index = 4)
    private String p5;

    @ExcelProperty(value = {"表头6","表头61","表头611"},index = 5)
    private String p6;

    @ExcelProperty(value = {"表头6","表头61","表头612"},index = 6)
    private String p7;

    @ExcelProperty(value = {"表头6","表头62","表头621"},index = 7)
    private String p8;

    @ExcelProperty(value = {"表头6","表头62","表头622"},index = 8)
    private String p9;
}

2. 导出的 Excel 拥有多个 sheet

调用 ExcelUtil.writeExcelWithSheets() 处理第一个 sheet,之后调用 write() 方法依次处理之后的 sheet,最后使用 finish() 方法结束

public void writeExcelWithSheets(HttpServletResponse response) throws IOException {
    List<ExportInfo> list = getList();
    String fileName = "一个 Excel 文件";
    String sheetName1 = "第一个 sheet";
    String sheetName2 = "第二个 sheet";
    String sheetName3 = "第三个 sheet";

    ExcelUtil.writeExcelWithSheets(response, list, fileName, sheetName1, new ExportInfo())
                .write(list, sheetName2, new ExportInfo())
                .write(list, sheetName3, new ExportInfo())
                .finish();
}

write 方法的参数为当前 sheet 的 list 数据,当前 sheet 名以及对应的映射类

 

好的,根据您的问题,我假设您已经使用EasyExcel成功解析了Excel表格,并且得到了解析结果。现在您需要将解析结果添加到原Excel表格的每一行后,并将更新后的数据导出指定的目标中。可以按照以下步骤操作: 1. 定义要添加的解析结果 假设您要将解析结果添加到每一行的最后一列之后,可以定义一个List,存放解析结果: ``` List<String> result = new ArrayList<>(); result.add("解析结果1"); result.add("解析结果2"); result.add("解析结果3"); // ... ``` 2. 读取Excel表格 使用EasyExcel读取Excel表格,得到Excel表格的数据: ``` // 定义Excel读取监听器 ReadListener listener = new ReadListener() { // 实现onRead方法,在读取到每一行数据时调用 @Override public void onRead(List<Object> data, AnalysisContext context) { // 处理读取到的数据,并将解析结果添加到每一行的最后一列之后 int lastIndex = data.size() - 1; data.add(lastIndex + 1, result.get(context.readRowHolder().getRowIndex())); } }; // 使用EasyExcel读取Excel表格 ExcelReader reader = EasyExcel.read("input.xlsx", listener).build(); reader.read(); ``` 3. 写出更新后的数据 使用EasyExcel的write方法,将更新后的数据写出到指定的目标中,例如写出到本地文件: ``` // 创建ExcelWriter对象 ExcelWriter writer = EasyExcel.write("output.xlsx").build(); // 写出数据到Excel文件中 writer.write(data, sheet); // 关闭ExcelWriter对象 writer.finish(); ``` 其中,data表示更新后的数据,sheet表示要更新的sheet。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值