利用Alibaba Easyexcel读取Excel

欢迎大家关注本博,同时欢迎大家评论交流,可以给个赞哦!!!

  Alibaba Easyexcel 简介:

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

  Alibaba Easyexcel 工具类整合:

  话不多少,直接上干货,在Alibaba Easyexcel 之上进行进一步抽象。

  · EasyExcelUtil类

package com.arhorchin.securitit.files.easyexcel;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.excel.ExcelReader;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.support.ExcelTypeEnum;

/**
 * @author Securitit.
 * @note Alibaba EasyExcel 封装类.
 */
public class EasyExcelUtil {

	/**
	 * logger.
	 */
	private static Logger logger = LoggerFactory.getLogger(EasyExcelUtil.class);

	/**
	 * 读取 Excel(多个 sheet).
	 *
	 * @param excelBts
	 *            Excel文件.
	 * @param rowModel
	 *            实体类映射,继承 BaseRowModel 类.
	 * @return Excel 数据 list.
	 */
	public static List<Object> readExcel(byte[] excelBts, BaseRowModel rowModel) {
		List<Object> retList = null;
		ExcelReader excelReader = null;
		EasyExcelListener easyExcelListener = null;

		try {
			retList = new ArrayList<Object>();
			easyExcelListener = new EasyExcelListener();
			excelReader = getReader(excelBts, easyExcelListener);
			if (null == excelReader) {
				return retList;
			}
			for (Sheet sheet : excelReader.getSheets()) {
				if (null != rowModel) {
					sheet.setClazz(rowModel.getClass());
				}
				excelReader.read(sheet);
			}
			retList = easyExcelListener.getDatas();
		} catch (Exception ex) {
			logger.error("EasyExcelUtil.readExcel.初始化Excel文件异常.", ex);
		}
		return retList;
	}

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

	/**
	 * 读取某个 sheet 的 Excel.
	 *
	 * @param excelBts
	 *            Excel文件.
	 * @param rowModel
	 *            实体类映射,继承 BaseRowModel 类.
	 * @param sheetNo
	 *            sheet 的序号 从1开始.
	 * @param headLineNum
	 *            表头行数,默认为1.
	 * @return Excel 数据 list.
	 */
	public static List<Object> readExcel(byte[] excelBts, BaseRowModel rowModel, int sheetNo, int headLineNum) {
		List<Object> retList = null;
		ExcelReader excelReader = null;
		EasyExcelListener easyExcelListener = null;

		try {
			retList = new ArrayList<Object>();
			easyExcelListener = new EasyExcelListener();
			excelReader = getReader(excelBts, easyExcelListener);
			if (null == excelReader) {
				return retList;
			}
			excelReader.read(new Sheet(sheetNo, headLineNum, rowModel.getClass()));
			retList = easyExcelListener.getDatas();
		} catch (Exception ex) {
			logger.error("EasyExcelUtil.readExcel.初始化Excel文件异常.", ex);
		}
		return retList;
	}

	/**
	 * 导出 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 EasyExcelWriterFactroy writeExcelWithSheets(HttpServletResponse response,
			List<? extends BaseRowModel> list, String fileName, String sheetName, BaseRowModel object) {
		EasyExcelWriterFactroy writer = new EasyExcelWriterFactroy(getOutputStream(fileName, response),
				ExcelTypeEnum.XLSX);
		Sheet sheet = new Sheet(1, 0, object.getClass());
		sheet.setSheetName(sheetName);
		writer.write(list, sheet);
		return writer;
	}

	/**
	 * 导出文件时为Writer生成OutputStream.
	 * 
	 * @param fileName
	 *            .
	 * @param response
	 *            .
	 * @return.
	 */
	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 EasyExcelException("创建文件失败!");
		}
	}

	/**
	 * 返回 ExcelReader.
	 *
	 * @param excelBts
	 *            Excel文件.
	 * @param EasyExcelListener
	 *            new EasyExcelListener().
	 */
	private static ExcelReader getReader(byte[] excelBts, EasyExcelListener easyExcelListener) {
		InputStream excelBais = null;
		try {
			excelBais = new ByteArrayInputStream(excelBts);
			return new ExcelReader(excelBais, null, easyExcelListener, false);
		} catch (Exception ex) {
			logger.error("EasyExcelUtil.getReader.获取Excel文件InputStream异常.", ex);
		}
		return null;
	}

}

  · EasyExcelListener类

package com.arhorchin.securitit.files.easyexcel;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

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

/**
 * @author Securitit.
 * @note EasyExcel解析监听器.
 */
@SuppressWarnings("rawtypes")
public class EasyExcelListener extends AnalysisEventListener {

	/**
	 * logger.
	 */
	private static Logger logger = LoggerFactory.getLogger(EasyExcelListener.class);

	/**
	 * 已解析数据存储.
	 */
	private List<Object> datas = new ArrayList<>();

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

	@Override
	public void doAfterAllAnalysed(AnalysisContext context) {
		logger.info("EasyExcelListener.doAfterAllAnalysed.解析Excel完毕.");
	}

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

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

}

  · EasyExcelWriterFactroy类

package com.arhorchin.securitit.files.easyexcel;

import java.io.IOException;
import java.io.OutputStream;
import java.util.List;

import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.metadata.BaseRowModel;
import com.alibaba.excel.metadata.Sheet;
import com.alibaba.excel.support.ExcelTypeEnum;

/**
 * @author Securitit.
 * @note EasyExcel 写入.
 */
public class EasyExcelWriterFactroy extends ExcelWriter {

    private int sheetNo = 1;

    private OutputStream outputStream;

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

    /**
     * 写入行数据.
     * @param list 数据行.
     * @param sheetName sheet页名称.
     * @param object . @return.
     */
    public EasyExcelWriterFactroy 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();
        }
    }

}

  · EasyExcelException类

package com.arhorchin.securitit.files.easyexcel;

/**
 * @author Securitit.
 * @note EasyExcel自定义异常.
 */
public class EasyExcelException extends RuntimeException {
	
	/**
	 * serialVersionUID.
	 */
	private static final long serialVersionUID = -7099423768629232473L;

	public EasyExcelException(String message) {
		super(message);
	}
	
}

  上面这几个类是读取和写入Excel的工具类,可以复制直接使用。

  Alibaba Easyexcel 测试:

  测试之前,我们首先编写一Excel,用于存储数据,本文测试使用的Excel如下:
在这里插入图片描述
  接着,需要新建BaseRowModel的子类,用于匹配Excel中对应数据项,本文使用TestRowModel,如下:

package com.arhorchin.securitit.files.easyexcel;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.metadata.BaseRowModel;

/**
 * @author Securitit.
 * @note EasyExcelTester RowModel.
 */
public class TestRowModel extends BaseRowModel {

    /**
     * 序号.
     */
    @ExcelProperty(
            index = 0)
    private String index;

    /**
     * 姓名.
     */
    @ExcelProperty(
            index = 1)
    private String name;

    /**
     * 年龄.
     */
    @ExcelProperty(
            index = 2)
    private String gmfdz;

    /**
     * 住址.
     */
    @ExcelProperty(
            index = 3)
    private String address;

    public String getIndex() {
        return index;
    }

    public void setIndex(String index) {
        this.index = index;
    }

    public String getName() {
        return name;
    }

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

    public String getGmfdz() {
        return gmfdz;
    }

    public void setGmfdz(String gmfdz) {
        this.gmfdz = gmfdz;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
    
}

  最后,通过上面已有内容,在测试中将Excel数据读取到JavaBean中:

package com.arhorchin.securitit.files.easyexcel;

import java.io.File;
import java.util.List;

import org.apache.commons.io.FileUtils;

import com.alibaba.fastjson.JSON;

/**
 * @author Securitit.
 * @note EasyExcelTester测试类.
 */
public class EasyExcelTester {

    public static void main(String[] args) throws Exception {
        String excelPath = null;
        byte[] excelBytes = null;
        TestRowModel rowModel = null;
        List<Object> parseObjList = null;

        rowModel = new TestRowModel();
        excelPath = "K:/files/easyexcel/EasyExcelTester.xlsx";
        excelBytes = FileUtils.readFileToByteArray(new File(excelPath));
        parseObjList = EasyExcelUtil.readExcel(excelBytes, rowModel);
        System.out.println(JSON.toJSONString(parseObjList));
    }

}

  测试类输出结果:

[{"address":"辽宁","cellStyleMap":{},"gmfdz":"18","index":"1","name":"张三"},{"address":"北京","cellStyleMap":{},"gmfdz":"23","index":"2","name":"李四"},{"address":"四川","cellStyleMap":{},"gmfdz":"43","index":"3","name":"赵五"}]

  可以看到,Excel中内容已读取到JavaBean中,其中cellStyleMap属性是BaseRowModel的属性,对实际操作不会产生影响。

  总结:

  Alibaba Easyexcel的操作相对比较简单,尤其处理大数据时,是首选,可以避免Excel操作给应用带来的压力。

本博微信公众号“超哥说码”,欢迎大家订阅,公众号正在完善中,会及时将更优质的博文推送于您!
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/securitit/article/details/107964530