发布时间:2023-07-09 16:30
在实际的业务系统开发过程中,操作 Excel 实现数据的导入导出基本上是个非常常见的需求。
之前,我们有介绍一款非常好用的工具:EasyPoi,有读者提出在数据量大的情况下,EasyPoi 会占用内存大,性能不够好,严重的时候,还会出现内存异常的现象。
今天我给大家推荐一款性能更好的 Excel 导入导出工具:EasyExcel,希望对大家有所帮助!
easyexcel 是阿里开源的一款 Excel导入导出工具,具有处理速度快、占用内存小、使用方便的特点,底层逻辑也是基于 apache poi 进行二次开发的,目前的应用也是非常广!
相比 EasyPoi,EasyExcel 的处理数据性能非常高,读取 75M (46W行25列) 的Excel,仅需使用 64M 内存,耗时 20s,极速模式还可以更快!
废话也不多说了,下面直奔主题!
在 SpringBoot 项目中集成 EasyExcel 其实非常简单,仅需一个依赖即可。
com.alibaba easyexcel 3.0.5
EasyExcel 的导出导入支持两种方式进行处理
下面我们以用户信息的导出导入为例,分别介绍两种处理方式。
简单导出
首先,我们只需要创建一个 UserEntity
用户实体类,然后添加对应的注解字段即可,示例代码如下:
public class UserWriteEntity { @ExcelProperty(value = \"姓名\") private String name; @ExcelProperty(value = \"年龄\") private int age; @DateTimeFormat(\"yyyy-MM-dd HH:mm:ss\") @ExcelProperty(value = \"操作时间\") private Date time; //set、get... }
然后,使用 EasyExcel 提供的 EasyExcel
工具类,即可实现文件的导出。
public static void main(String[] args) throws FileNotFoundException { ListdataList = new ArrayList<>(); for (int i = 0; i < 10; i++) { UserWriteEntity userEntity = new UserWriteEntity(); userEntity.setName(\"张三\" + i); userEntity.setAge(20 + i); userEntity.setTime(new Date(System.currentTimeMillis() + i)); dataList.add(userEntity); } //定义文件输出位置 FileOutputStream outputStream = new FileOutputStream(new File(\"/Users/panzhi/Documents/easyexcel-export-user1.xlsx\")); EasyExcel.write(outputStream, UserWriteEntity.class).sheet(\"用户信息\").doWrite(dataList); }
运行程序,打开文件内容结果!
简单导入
这种简单固定表头的 Excel 文件,如果想要读取文件数据,操作也很简单。
以上面的导出文件为例,使用 EasyExcel 提供的 EasyExcel
工具类,即可来实现文件内容数据的快速读取,示例代码如下:
首先创建读取实体类
/** * 读取实体类 */ public class UserReadEntity { @ExcelProperty(value = \"姓名\") private String name; /** * 强制读取第三个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配 */ @ExcelProperty(index = 1) private int age; @DateTimeFormat(\"yyyy-MM-dd HH:mm:ss\") @ExcelProperty(value = \"操作时间\") private Date time; //set、get... }
然后读取文件数据,并封装到对象里面
public static void main(String[] args) throws FileNotFoundException { //同步读取文件内容 FileInputStream inputStream = new FileInputStream(new File(\"/Users/panzhi/Documents/easyexcel-user1.xls\")); Listlist = EasyExcel.read(inputStream).head(UserReadEntity.class).sheet().doReadSync(); System.out.println(JSONArray.toJSONString(list)); }
运行程序,输出结果如下:
[{\"age\":20,\"name\":\"张三0\",\"time\":1616920360000},{\"age\":21,\"name\":\"张三1\",\"time\":1616920360000},{\"age\":22,\"name\":\"张三2\",\"time\":1616920360000},{\"age\":23,\"name\":\"张三3\",\"time\":1616920360000},{\"age\":24,\"name\":\"张三4\",\"time\":1616920360000},{\"age\":25,\"name\":\"张三5\",\"time\":1616920360000},{\"age\":26,\"name\":\"张三6\",\"time\":1616920360000},{\"age\":27,\"name\":\"张三7\",\"time\":1616920360000},{\"age\":28,\"name\":\"张三8\",\"time\":1616920360000},{\"age\":29,\"name\":\"张三9\",\"time\":1616920360000}]
动态自由导出导入
在实际使用开发中,我们不可能每来一个 excel 导入导出需求,就编写一个实体类,很多业务需求需要根据不同的字段来动态导入导出,没办法基于实体类注解的方式来读取文件或者写入文件。
因此,基于 EasyExcel
提供的动态参数化生成文件和动态监听器读取文件方法,我们可以单独封装一套动态导出导出工具类,省的我们每次都需要重新编写大量重复工作,以下就是小编我在实际使用过程,封装出来的工具类,在此分享给大家!
public class DynamicEasyExcelExportUtils { private static final Logger log = LoggerFactory.getLogger(DynamicEasyExcelExportUtils.class); private static final String DEFAULT_SHEET_NAME = \"sheet1\"; /** * 动态生成导出模版(单表头) * @param headColumns 列名称 * @return excel文件流 */ public static byte[] exportTemplateExcelFile(ListheadColumns){ List > excelHead = Lists.newArrayList(); headColumns.forEach(columnName -> { excelHead.add(Lists.newArrayList(columnName)); }); byte[] stream = createExcelFile(excelHead, new ArrayList<>()); return stream; } /** * 动态生成模版(复杂表头) * @param excelHead 列名称 * @return */ public static byte[] exportTemplateExcelFileCustomHead(List
> excelHead){ byte[] stream = createExcelFile(excelHead, new ArrayList<>()); return stream; } /** * 动态导出文件(通过map方式计算) * @param headColumnMap 有序列头部 * @param dataList 数据体 * @return */ public static byte[] exportExcelFile(LinkedHashMap
headColumnMap, List
/** * 创建一个文件读取监听器 */ public class DynamicEasyExcelListener extends AnalysisEventListener
/** * 编写导入工具类 */ public class DynamicEasyExcelImportUtils { /** * 动态获取全部列和数据体,默认从第一行开始解析数据 * @param stream * @return */ public static List> parseExcelToView(byte[] stream) { return parseExcelToView(stream, 1); } /** * 动态获取全部列和数据体 * @param stream excel文件流 * @param parseRowNumber 指定读取行 * @return */ public static List > parseExcelToView(byte[] stream, Integer parseRowNumber) { DynamicEasyExcelListener readListener = new DynamicEasyExcelListener(); EasyExcelFactory.read(new ByteArrayInputStream(stream)).registerReadListener(readListener).headRowNumber(parseRowNumber).sheet(0).doRead(); List > headList = readListener.getHeadList(); if(CollectionUtils.isEmpty(headList)){ throw new RuntimeException(\"Excel未包含表头\"); } List > dataList = readListener.getDataList(); if(CollectionUtils.isEmpty(dataList)){ throw new RuntimeException(\"Excel未包含数据\"); } //获取头部,取最后一次解析的列头数据 Map excelHeadIdxNameMap = headList.get(headList.size() -1); //封装数据体 List > excelDataList = Lists.newArrayList(); for (Map dataRow : dataList) { Map rowData = new LinkedHashMap<>(); excelHeadIdxNameMap.entrySet().forEach(columnHead -> { rowData.put(columnHead.getValue(), dataRow.get(columnHead.getKey())); }); excelDataList.add(rowData); } return excelDataList; } /** * 文件导入测试 * @param args * @throws IOException */ public static void main(String[] args) throws IOException { FileInputStream inputStream = new FileInputStream(new File(\"/Users/panzhi/Documents/easyexcel-export-user5.xlsx\")); byte[] stream = IoUtils.toByteArray(inputStream); List > dataList = parseExcelToView(stream, 2); System.out.println(JSONArray.toJSONString(dataList)); inputStream.close(); } }
为了方便后续的操作流程,在解析数据的时候,会将列名作为 key
!
在实际的业务开发过程中,根据参数动态实现 Excel 的导出导入还是非常广的。
当然,EasyExcel 的功能还不只上面介绍的那些内容,还有基于模版进行 excel的填充,web 端 restful 的导出导出,使用方法大致都差不多,更多的功能,会在后续的文章中再次介绍,如果有描述不对的地方,欢迎网友批评吐槽!