北京建网站找哪个公司,如何做公司网站优化,网站搭建说明,wordpress4.9安装出错01、背景介绍
在实际的业务系统开发过程中#xff0c;操作 Excel 实现数据的导入导出基本上是个非常常见的需求。
之前#xff0c;我们有介绍一款非常好用的工具#xff1a;EasyPoi#xff0c;有读者提出在数据量大的情况下#xff0c;EasyPoi 会占用内存大#xff0c;…01、背景介绍
在实际的业务系统开发过程中操作 Excel 实现数据的导入导出基本上是个非常常见的需求。
之前我们有介绍一款非常好用的工具EasyPoi有读者提出在数据量大的情况下EasyPoi 会占用内存大性能不够好严重的时候还会出现内存异常的现象。
今天我给大家推荐一款性能更好的 Excel 导入导出工具EasyExcel希望对大家有所帮助
easyexcel 是阿里开源的一款 Excel导入导出工具具有处理速度快、占用内存小、使用方便的特点底层逻辑也是基于 apache poi 进行二次开发的目前的应用也是非常广 相比 EasyPoiEasyExcel 的处理数据性能非常高读取 75M (46W行25列) 的Excel仅需使用 64M 内存耗时 20s极速模式还可以更快 废话也不多说了下面直奔主题
02、方案实践
在 SpringBoot 项目中集成 EasyExcel 其实非常简单仅需一个依赖即可。
!--EasyExcel相关依赖--
dependencygroupIdcom.alibaba/groupIdartifactIdeasyexcel/artifactIdversion3.0.5/version
/dependencyEasyExcel 的导出导入支持两种方式进行处理 第一种是通过实体类注解方式来生成文件和反解析文件数据映射成对象 第二种是通过动态参数化生成文件和反解析文件数据
下面我们以用户信息的导出导入为例分别介绍两种处理方式。
2.1、简单导出
首先我们只需要创建一个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 {ListUserWriteEntity dataList 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);
}运行程序打开文件内容结果 2.2、简单导入
这种简单固定表头的 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));ListUserReadEntity list 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}]2.3、动态自由导出导入
在实际使用开发中我们不可能每来一个 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(ListString headColumns){ListListString 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(ListListString excelHead){byte[] stream createExcelFile(excelHead, new ArrayList());return stream;}/*** 动态导出文件通过map方式计算* param headColumnMap 有序列头部* param dataList 数据体* return*/public static byte[] exportExcelFile(LinkedHashMapString, String headColumnMap, ListMapString, Object dataList){//获取列名称ListListString excelHead new ArrayList();if(MapUtils.isNotEmpty(headColumnMap)){//key为匹配符value为列名如果多级列名用逗号隔开headColumnMap.entrySet().forEach(entry - {excelHead.add(Lists.newArrayList(entry.getValue().split(,)));});}ListListObject excelRows new ArrayList();if(MapUtils.isNotEmpty(headColumnMap) CollectionUtils.isNotEmpty(dataList)){for (MapString, Object dataMap : dataList) {ListObject rows new ArrayList();headColumnMap.entrySet().forEach(headColumnEntry - {if(dataMap.containsKey(headColumnEntry.getKey())){Object data dataMap.get(headColumnEntry.getKey());rows.add(data);}});excelRows.add(rows);}}byte[] stream createExcelFile(excelHead, excelRows);return stream;}/*** 生成文件自定义头部排列* param rowHeads* param excelRows* return*/public static byte[] customerExportExcelFile(ListListString rowHeads, ListListObject excelRows){//将行头部转成easyexcel能识别的部分ListListString excelHead transferHead(rowHeads);return createExcelFile(excelHead, excelRows);}/*** 生成文件* param excelHead* param excelRows* return*/private static byte[] createExcelFile(ListListString excelHead, ListListObject excelRows){try {if(CollectionUtils.isNotEmpty(excelHead)){ByteArrayOutputStream outputStream new ByteArrayOutputStream();EasyExcel.write(outputStream).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).head(excelHead).sheet(DEFAULT_SHEET_NAME).doWrite(excelRows);return outputStream.toByteArray();}} catch (Exception e) {log.error(动态生成excel文件失败headColumns JSONArray.toJSONString(excelHead) excelRows JSONArray.toJSONString(excelRows), e);}return null;}/*** 将行头部转成easyexcel能识别的部分* param rowHeads* return*/public static ListListString transferHead(ListListString rowHeads){//将头部列进行反转ListListString realHead new ArrayList();if(CollectionUtils.isNotEmpty(rowHeads)){MapInteger, ListString cellMap new LinkedHashMap();//遍历行for (ListString cells : rowHeads) {//遍历列for (int i 0; i cells.size(); i) {if(cellMap.containsKey(i)){cellMap.get(i).add(cells.get(i));} else {cellMap.put(i, Lists.newArrayList(cells.get(i)));}}}//将列一行一行加入realHeadcellMap.entrySet().forEach(item - realHead.add(item.getValue()));}return realHead;}/*** 导出文件测试* param args* throws IOException*/public static void main(String[] args) throws IOException {//导出包含数据内容的文件方式一LinkedHashMapString, String headColumnMap Maps.newLinkedHashMap();headColumnMap.put(className,班级);headColumnMap.put(name,学生信息,姓名);headColumnMap.put(sex,学生信息,性别);ListMapString, Object dataList new ArrayList();for (int i 0; i 5; i) {MapString, Object dataMap Maps.newHashMap();dataMap.put(className, 一年级);dataMap.put(name, 张三 i);dataMap.put(sex, 男);dataList.add(dataMap);}byte[] stream1 exportExcelFile(headColumnMap, dataList);FileOutputStream outputStream1 new FileOutputStream(new File(/Users/panzhi/Documents/easyexcel-export-user5.xlsx));outputStream1.write(stream1);outputStream1.close();//导出包含数据内容的文件方式二//头部第一层ListString head1 new ArrayList();head1.add(第一行头部列1);head1.add(第一行头部列1);head1.add(第一行头部列1);head1.add(第一行头部列1);//头部第二层ListString head2 new ArrayList();head2.add(第二行头部列1);head2.add(第二行头部列1);head2.add(第二行头部列2);head2.add(第二行头部列2);//头部第三层ListString head3 new ArrayList();head3.add(第三行头部列1);head3.add(第三行头部列2);head3.add(第三行头部列3);head3.add(第三行头部列4);//封装头部ListListString allHead new ArrayList();allHead.add(head1);allHead.add(head2);allHead.add(head3);//封装数据体//第一行数据ListObject data1 Lists.newArrayList(1,1,1,1);//第二行数据ListObject data2 Lists.newArrayList(2,2,2,2);ListListObject allData Lists.newArrayList(data1, data2);byte[] stream2 customerExportExcelFile(allHead, allData);FileOutputStream outputStream2 new FileOutputStream(new File(/Users/panzhi/Documents/easyexcel-export-user6.xlsx));outputStream2.write(stream2);outputStream2.close();}
}然后编写一个动态导入工具类
/*** 创建一个文件读取监听器*/
public class DynamicEasyExcelListener extends AnalysisEventListenerMapInteger, String {private static final Logger LOGGER LoggerFactory.getLogger(UserDataListener.class);/*** 表头数据存储所有的表头数据*/private ListMapInteger, String headList new ArrayList();/*** 数据体*/private ListMapInteger, String dataList new ArrayList();/*** 这里会一行行的返回头** param headMap* param context*/Overridepublic void invokeHeadMap(MapInteger, String headMap, AnalysisContext context) {LOGGER.info(解析到一条头数据:{}, JSON.toJSONString(headMap));//存储全部表头数据headList.add(headMap);}/*** 这个每一条数据解析都会来调用** param data* one row value. Is is same as {link AnalysisContext#readRowHolder()}* param context*/Overridepublic void invoke(MapInteger, String data, AnalysisContext context) {LOGGER.info(解析到一条数据:{}, JSON.toJSONString(data));dataList.add(data);}/*** 所有数据解析完成了 都会来调用** param context*/Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 这里也要保存数据确保最后遗留的数据也存储到数据库LOGGER.info(所有数据解析完成);}public ListMapInteger, String getHeadList() {return headList;}public ListMapInteger, String getDataList() {return dataList;}
}动态导入工具类
/*** 编写导入工具类*/
public class DynamicEasyExcelImportUtils {/*** 动态获取全部列和数据体默认从第一行开始解析数据* param stream* return*/public static ListMapString,String parseExcelToView(byte[] stream) {return parseExcelToView(stream, 1);}/*** 动态获取全部列和数据体* param stream excel文件流* param parseRowNumber 指定读取行* return*/public static ListMapString,String parseExcelToView(byte[] stream, Integer parseRowNumber) {DynamicEasyExcelListener readListener new DynamicEasyExcelListener();EasyExcelFactory.read(new ByteArrayInputStream(stream)).registerReadListener(readListener).headRowNumber(parseRowNumber).sheet(0).doRead();ListMapInteger, String headList readListener.getHeadList();if(CollectionUtils.isEmpty(headList)){throw new RuntimeException(Excel未包含表头);}ListMapInteger, String dataList readListener.getDataList();if(CollectionUtils.isEmpty(dataList)){throw new RuntimeException(Excel未包含数据);}//获取头部,取最后一次解析的列头数据MapInteger, String excelHeadIdxNameMap headList.get(headList.size() -1);//封装数据体ListMapString,String excelDataList Lists.newArrayList();for (MapInteger, String dataRow : dataList) {MapString,String 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);ListMapString,String dataList parseExcelToView(stream, 2);System.out.println(JSONArray.toJSONString(dataList));inputStream.close();}
}为了方便后续的操作流程在解析数据的时候会将列名作为key
03、小结
在实际的业务开发过程中根据参数动态实现 Excel 的导出导入还是非常广的。
当然EasyExcel 的功能还不只上面介绍的那些内容还有基于模版进行 excel的填充web 端 restful 的导出导出使用方法大致都差不多