导入导出Excel
Springboot + Easyexcel导入导出excel
- EasyExcel 的导出导入支持两种方式进行处理
- *easyexcel 导出不用监听器,导入需要写监听器*
- 一、导入:简单实现
- 1. 导入依赖,阿里的easyexcel插件
- 2. 程序
- 2-1. 实体类:
- 2-2. 定义一个 监听类:
- 2-3. service:
- 2-4. Controller 上传文件接口
- 3. Postman测试
- 二、导入:多头行数,多sheet,复杂表头
- 1. 两sheet表头数据不一致
- *思路:需要定义各自的excel接收数据的实体类,然后创建各自的监听类,重写方法*
- 具体实现
- 监听类
- controller层
- service实现层
- 多行头
- 读取时设置头行数即可
- 读取表头数据
- 三、导出:简单简单实现
- 1. 模拟数据 10条数据
- 3. 程序
- 3. 过滤导出列
- 五、导入:复杂头 合并表头
- 1. 实体类
- 六、动态自由导出导入
- 1. 导出
- 1-1. 编写一个动态导出工具类
- 1-2. 测试: *easyexcel-export-user1.xlsx*
- 接上面同一个类里 方法二
- 2. 导入: EasyExcel 导入需要一个监听器,导出不需要
- 2-1. 编写一个动态导入监听器
- 2-2. 编写动态导入工具类
EasyExcel 的导出导入支持两种方式进行处理
easyexcel 导出不用监听器,导入需要写监听器
- 第一种是通过实体类注解方式来生成文件和反解析文件数据映射成对象
- 第二种是通过动态参数化生成文件和反解析文件数据
下面我们以用户信息的导出导入为例,分别介绍两种处理方式。
一、导入:简单实现
1. 导入依赖,阿里的easyexcel插件
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>2.1.6</version>
</dependency
创建一个用来 读取 excel的实体类
实体类的属性可以用
-
@ExcelProperty(index = 0),index=0,找的是上图 A列(第一列)
-
@ExcelProperty(value = “标号”)
两种都可以用,但是不要两个一起用
2. 程序
2-1. 实体类:
实体类中可以使用@DateFormat(阿里包下的)注解:
要使用String类型来接收数据才有用
@Data
public class TemplateEntity {@ExcelProperty("标号")private Integer label;@ExcelProperty("字符串")private String str;@ExcelProperty("数字")private Integer num;@ExcelProperty("时间")// 这里需要用string接收才会格式化@DateTimeFormat("yyyy-MM-dd")private String date;}
————————————————
2-2. 定义一个 监听类:
public class TemplateListener extends AnalysisEventListener<TemplateEntity> {private List<TemplateEntity> list = new ArrayList<>();// 一条一条读取数据,全部添加到list集合里@Overridepublic void invoke(TemplateEntity data, AnalysisContext analysisContext) {list.add(data);}@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {}public List<TemplateEntity> getData() {return list;}
}
2-3. service:
public interface TemplateService {/*** 导入excel*/Result importExcel(MultipartFile file) throws IOException;
}@Service
public class TemplateServiceImpl implements TemplateService {@Overridepublic Result importExcel(MultipartFile file) throws IOException{List<TemplateEntity> entities = getTemplateEntities(file);// 处理数据System.out.println(entities);return Result.success(entities);}// 读取 excel 数据private List<TemplateEntity> getTemplateEntities(MultipartFile file) throws IOException {TemplateListener listener = new TemplateListener(); // 定义的 listenerEasyExcel.read(file.getInputStream(), TemplateEntity.class, listener).sheet().doRead();// 返回 所有数据return listener.getData();}
}
2-4. Controller 上传文件接口
@RestController
@RequestMapping("/sys")
public class TemplateController {@Autowiredprivate TemplateService templateService;@RequestMapping("/import")public Result importData(@RequestPart("file") MultipartFile file) throws IOException{return templateService.importExcel(file);}
}
3. Postman测试
{"code": 200,"msg": "处理成功","data": [{"label": 1,"str": "a","num": 20},{"label": 2,"str": "b","num": 30},{"label": 3,"str": "c","num": 40},...
}
————————————————
二、导入:多头行数,多sheet,复杂表头
1. 两sheet表头数据不一致
这里为了演示效果,sheet1和sheet3是不同表头的,sheet2目前是空的数据表
思路:需要定义各自的excel接收数据的实体类,然后创建各自的监听类,重写方法
读取时,指定不同的监听类,excel接收数据的实体类对象,然后放入map中返回即可
具体实现
实体类
TemplateEntity接收sheet1
@Data
public class TemplateEntity {@ExcelProperty("标号")private Integer label;@ExcelProperty("字符串")private String str;@ExcelProperty("数字")private Integer num;@ExcelProperty(value = "时间")@DateTimeFormat("yyyy-MM-dd")private String date;
}
OtherTemplateEntity接收sheet3
@Data
public class OtherTemplateEntity {@ExcelProperty("标号")private String label;@ExcelProperty("名称")private String name;@ExcelProperty("类型")private String type;@ExcelProperty(value = "时间")@DateTimeFormat("yyyy-MM-dd")private String date;
}
监听类
同上,只是写两个各自的
controller层
@PostMapping("/importMany")
public R importMany(@RequestPart("file") MultipartFile file) throws IOException {return easyExcelService.importManyExcel(file);
}
service实现层
public R importManyExcel(MultipartFile file) throws IOException {Map<String, Object> map = getTemplateEntitiesMany(file);List<TemplateEntity> data1 = (List<TemplateEntity>) map.get("data1");List<OtherTemplateEntity> data2 = (List<OtherTemplateEntity>) map.get("data2");log.info("data1数据=={}", data1);log.info("data2数据=={}", data2);return R.success(map);
}private Map<String, Object> getTemplateEntitiesMany(MultipartFile file) throws IOException {Map<String,Object> map = new HashMap<>();TemplateListener listener = new TemplateListener(); // 定义的 listenerOtherTemplateListener otherListener = new OtherTemplateListener();ExcelReader excelReader = EasyExcel.read(file.getInputStream()).build();// 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener// readSheet参数设置读取sheet的序号// 读取sheet1ReadSheet readSheet1 =EasyExcel.readSheet(0).head(TemplateEntity.class).registerReadListener(listener).build();// 读取sheet3ReadSheet readSheet2 =EasyExcel.readSheet(2).head(OtherTemplateEntity.class).registerReadListener(otherListener).build();excelReader.read(readSheet1, readSheet2);// 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的excelReader.finish();// 取出数据放入map中,然后返回List<TemplateEntity> data1 = listener.getData();List<OtherTemplateEntity> data2 = otherListener.getData();map.put("data1", data1);map.put("data2", data2);return map;}
{"code": 200,"msg": "OK","message": null,"data": {"data2": [{"label": "a","name": "a1","type": "t1","date": "2022-01-07"},{"label": "b","name": "b1","type": "t2","date": "2022-01-07"}......],"data1": [{"label": 1,"str": "a","num": 20,"date": "2021-12-20"},{"label": 2,"str": "b","num": 30,"date": "2021-12-20"}......]}
}
多行头
读取时设置头行数即可
headRowNumber是头行数,如下是设置头行数2,那么读取时会从第三行开始读取数据
private List<TemplateEntity> getTemplateEntities(MultipartFile file) throws IOException {TemplateListener listener = new TemplateListener(); // 定义的 listenerEasyExcel.read(file.getInputStream(), TemplateEntity.class, listener).sheet(0).headRowNumber(2).doRead();// 返回 所有数据return listener.getData();}
读取表头数据
在监听类中重写invokeHeadMap方法,将表头数据也添加即可
public class TemplateListener extends AnalysisEventListener<TemplateEntity> {private List<TemplateEntity> list = new ArrayList<>();@Overridepublic void invoke(TemplateEntity data, AnalysisContext context) {list.add(data);}@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {}public List<TemplateEntity> getData() {return list;}@Overridepublic void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {// 读取到头数据LOGGER.info("解析到一条头数据:{}", JSON.toJSONString(headMap))}
————————————————
三、导出:简单简单实现
实体类省略,还是上面的TemplateEntity
导出excel数据,这里有两种写法,拟定好文件名称直接传入方法,会自动创建一个文件
1. 模拟数据 10条数据
// 模拟数据
private List<TemplateEntity> exportData() {List<TemplateEntity> entities = new ArrayList<>();for (int i = 0; i< 10; i++) {TemplateEntity entity = new TemplateEntity();entity.setStr("字符串" + i);entity.setDate("数据" + i);entity.setLabel(i+1);entity.setNum(i);entities.add(entity);}return entities;
}
3. 程序
public R export() {String path = "C:\\Users\\EDZ\\Desktop\\";// 写法1String fileName = path + System.currentTimeMillis() + ".xlsx";// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭// 如果这里想使用03 则 传入excelType参数即可EasyExcel.write(fileName, TemplateEntity.class).sheet("模板").doWrite(exportData());// 写法2// 这里 需要指定写用哪个class去写ExcelWriter excelWriter = EasyExcel.write(fileName, TemplateEntity.class).build();WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();excelWriter.write(exportData(), writeSheet);// 千万别忘记finish 会帮忙关闭流excelWriter.finish();return R.success();
}
————————————————
3. 过滤导出列
public R export() {String path = "C:\\Users\\EDZ\\Desktop\\";String fileName = path + System.currentTimeMillis() + ".xlsx";// 加入要忽略date字段Set<String> excludeColumnFiledNames = new HashSet<String>();excludeColumnFiledNames.add("date");EasyExcel.write(fileName,TemplateEntity.class).excludeColumnFiledNames(excludeColumnFiledNames).sheet("模板").doWrite(exportData()); }
五、导入:复杂头 合并表头
1. 实体类
@Data
public class TemplateEntity {@ExcelProperty({"主标题", "标号"})private Integer label;@ExcelProperty({"主标题", "字符串"})private String str;@ExcelProperty({"主标题", "数字"})private Integer num;@ExcelProperty({"主标题", "时间"})@DateTimeFormat("yyyy-MM-dd")private String date;
}
六、动态自由导出导入
在实际使用开发中,我们不可能每来一个 excel 导入导出需求,就编写一个实体类,很多业务需求需要根据不同的字段来动态导入导出,没办法基于实体类注解的方式来读取文件或者写入文件。
因此,基于EasyExcel提供的动态参数化生成文件和动态监听器读取文件方法,我们可以单独封装一套动态导出导出工具类,省的我
们每次都需要重新编写大量重复工作,我在实际使用过程,封装出来的工具类如下:
<!-- guava 工具类 集合各种处理 --><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>20.0</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.5</version></dependency>
1. 导出
1-1. 编写一个动态导出工具类
package com.ltkj.common.excel;import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.ltkj.common.utils.JsonUtils;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;/*** 动态导出工具类测试** @author wangl* @date 2023-10-10*/
public class DynamicEasyExcelExportUtilTest {private static final Logger log = LoggerFactory.getLogger(DynamicEasyExcelExportUtilTest.class);private static final String DEFAULT_SHEET_NAME = "sheet1";/*** 动态生成导出模版(单表头)** @param headColumns 列名称* @return excel文件流*/public static byte[] exportTemplateExcelFile(List<String> headColumns) {List<List<String>> excelHead = Lists.newArrayList();byte[] stream = createExcelFile(excelHead, new ArrayList<>());return stream;}/*** 动态生成模版(复杂表头)** @param excelHead 列名称* @return*/public static byte[] exportTemplateExcelFileCustomHead(List<List<String>> excelHead) {byte[] stream = createExcelFile(excelHead, new ArrayList<>());return stream;}/*** 动态导出文件(通过map方式计算)** @param headColumnMap 有序列头部* @param dataList 数据体* @return*/public static byte[] exportExcelFile(LinkedHashMap<String, String> headColumnMap, List<Map<String, Object>> dataList) {//获取列名称List<List<String>> excelHead = new ArrayList<>();if (MapUtils.isNotEmpty(headColumnMap)) {//key为匹配符,value为列名,如果多级列名用逗号隔开headColumnMap.entrySet().forEach(entry -> {excelHead.add(Lists.newArrayList(entry.getValue().split(",")));});}List<List<Object>> excelRows = new ArrayList<>();if (MapUtils.isNotEmpty(headColumnMap) && CollectionUtils.isNotEmpty(dataList)) {for (Map<String, Object> dataMap : dataList) {List<Object> 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(List<List<String>> rowHeads, List<List<Object>> excelRows) {//将行头部转成easyexcel能识别的部分List<List<String>> excelHead = transferHead(rowHeads);return createExcelFile(excelHead, excelRows);}/*** 生成文件** @param excelHead* @param excelRows* @return*/private static byte[] createExcelFile(List<List<String>> excelHead, List<List<Object>> 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:" + JsonUtils.toJsonString(excelHead) + ",excelRows:" + JsonUtils.toJsonString(excelRows), e);}return null;}/*** 将行头部转成easyexcel能识别的部分** @param rowHeads* @return*/public static List<List<String>> transferHead(List<List<String>> rowHeads) {//将头部列进行反转List<List<String>> realHead = new ArrayList<>();if (CollectionUtils.isNotEmpty(rowHeads)) {Map<Integer, List<String>> cellMap = new LinkedHashMap<>();//遍历行for (List<String> 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 {//导出包含数据内容的文件(方式一)LinkedHashMap<String, String> headColumnMap = Maps.newLinkedHashMap();headColumnMap.put("name", "姓名");headColumnMap.put("sex", "性别");headColumnMap.put("titleName", "职称信息一, 职称名称");headColumnMap.put("titleLevel", "职称信息一, 职称等级");headColumnMap.put("titleName_2", "职称信息二, 职称名称");headColumnMap.put("titleLevel_2", "职称信息二, 职称等级");List<Map<String, Object>> dataList = new ArrayList<>();for (int i = 0; i < 5; i++) {Map<String, Object> dataMap = Maps.newHashMap();dataMap.put("name", "张三" + i);dataMap.put("sex", "男");dataMap.put("titleName", "会计师");dataMap.put("titleLevel", i + " 级");dataMap.put("titleName_2", "律师");dataMap.put("titleLevel_2", i * 10 + " 级");dataList.add(dataMap);}byte[] stream1 = exportExcelFile(headColumnMap, dataList);FileOutputStream outputStream1 = new FileOutputStream(new File("easyexcel-export-user1.xlsx"));outputStream1.write(stream1);outputStream1.close();}
}
1-2. 测试: easyexcel-export-user1.xlsx
接上面同一个类里 方法二
//导出包含数据内容的文件(方式二)//头部,第一层List<String> head1 = new ArrayList<>();head1.add("第1行头部列1-序号");head1.add("第1行头部列2-缴纳单位");head1.add("第1行头部列3-姓名");head1.add("第1行头部列4-身份证号");// 第一行:一直是列1 列1 列1:就是合并列的意思head1.add("第1行头部列5-五险一金信息");head1.add("第1行头部列5-五险一金信息");head1.add("第1行头部列5-五险一金信息");head1.add("第1行头部列5-五险一金信息");head1.add("第1行头部列5-五险一金信息");head1.add("第1行头部列5-五险一金信息");//头部,第二层List<String> head2 = new ArrayList<>();// 2行 和 1行 行头相同: 就是合并行的意思head2.add("第1行头部列1-序号");head2.add("第1行头部列2-缴纳单位");head2.add("第1行头部列3-姓名");// 2-3行合并,不和1行合并head2.add("第1行头部列3-身份证号");head2.add("第2行头部列5-养老保险");head2.add("第2行头部列5-养老保险");head2.add("第2行头部列5-养老保险");head2.add("第2行头部列8-医疗保险");head2.add("第2行头部列8-医疗保险");head2.add("第2行头部列8-医疗保险");//头部,第三层List<String> head3 = new ArrayList<>();// 3行 和 2行 行头相同: 就是合并行的意思head3.add("第1行头部列1-序号");head3.add("第1行头部列2-缴纳单位");head3.add("第1行头部列3-姓名");// 2-3行合并,不和1行合并head3.add("第1行头部列3-身份证号");head3.add("第3行头部列5-缴费基数");head3.add("单位缴纳比例");head3.add("第3行头部列7-个人缴纳比例");head3.add("第3行头部列8-缴费基数");head3.add("单位缴纳比例");head3.add("第3行头部列10-个人缴纳比例");//封装头部List<List<String>> allHead = new ArrayList<>();allHead.add(head1);allHead.add(head2);allHead.add(head3);//封装数据体//第一行数据List<Object> data1 = Lists.newArrayList("001", "旅投科技", "赵一", "130xxx1", 5000, 5, 6, 6000, 7, 8);//第二行数据List<Object> data2 = Lists.newArrayList("002", "三药集团", "孙二", "232xxx2", 8000, 8, 9, 9000, 10, 12);List<List<Object>> allData = Lists.newArrayList(data1, data2);byte[] stream2 = customerExportExcelFile(allHead, allData);FileOutputStream outputStream2 = new FileOutputStream(new File("easyexcel-export-user2.xlsx"));outputStream2.write(stream2);outputStream2.close();
2. 导入: EasyExcel 导入需要一个监听器,导出不需要
<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.1</version></dependency><!-- guava 工具类 --><dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>20.0</version></dependency></dependencies>
2-1. 编写一个动态导入监听器
package com.ltkj.common.excel;import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson2.JSON;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.util.ArrayList;
import java.util.List;
import java.util.Map;/*** EasyExcel 导入需要一个监听器,导出不需要** @author wangl* @date 2023-10-14*/
public class DynamicEasyExcelListener extends AnalysisEventListener<Map<Integer, String>> {private static final Logger LOGGER = LoggerFactory.getLogger(DynamicEasyExcelListener.class);/*** 表头数据(存储所有的表头数据)*/private List<Map<Integer, String>> headList = new ArrayList<>();/*** 数据体*/private List<Map<Integer, String>> dataList = new ArrayList<>();/*** 这里会一行行的返回头** @param headMap* @param context*/@Overridepublic void invokeHeadMap(Map<Integer, 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(Map<Integer, String> data, AnalysisContext context) {// 解析一条 放到 dataList里,最后返回 dataList 即可 LOGGER.info("解析到一条data数据:{}", JSON.toJSONString(data));dataList.add(data);}/*** 所有数据解析完成了 都会来调用** @param context*/@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库LOGGER.info("所有数据解析完成!");}public List<Map<Integer, String>> getHeadList() {return headList;}public List<Map<Integer, String>> getDataList() {return dataList;}
}
2-2. 编写动态导入工具类
package com.ltkj.common.excel;import com.ali
baba.excel.EasyExcelFactory;
import com.alibaba.excel.util.IoUtils;
import com.alibaba.fastjson.JSONArray;
import com.google.common.collect.Lists;
import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;/*** 编写导入工具类** @author wangl* @date 2023-10-14*/
public class DynamicEasyExcelImportUtils {private static final Logger LOGGER = LoggerFactory.getLogger(DynamicEasyExcelListener.class);/*** 动态获取全部列和数据体,默认只有1行标题** @param stream* @return*/public static List<Map<String, String>> parseExcelToView(byte[] stream) {return parseExcelToView(stream, 1);}/*** 动态获取全部列和数据体,指定有几行标题** @param stream excel文件流* @param parseRowNumber 指定有几行标题* @return*/public static List<Map<String, String>> parseExcelToView(byte[] stream, Integer parseRowNumber) {LOGGER.info("指定有[" + parseRowNumber + "]行标题");DynamicEasyExcelListener readListener = new DynamicEasyExcelListener();EasyExcelFactory.read(new ByteArrayInputStream(stream)).registerReadListener(readListener).headRowNumber(parseRowNumber).sheet("sheet1").doRead();List<Map<Integer, String>> headList = readListener.getHeadList();if (CollectionUtils.isEmpty(headList)) {throw new RuntimeException("Excel未包含表头");}List<Map<Integer, String>> dataList = readListener.getDataList();if (CollectionUtils.isEmpty(dataList)) {throw new RuntimeException("Excel未包含数据");}//获取头部,取最后一次解析的列头数据Map<Integer, String> excelHeadIdxNameMap = headList.get(headList.size() - 1);//封装数据体List<Map<String, String>> excelDataList = Lists.newArrayList();for (Map<Integer, String> dataRow : dataList) {Map<String, 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("easyexcel-export-user1.xlsx"));byte[] stream = IoUtils.toByteArray(inputStream);List<Map<String, String>> dataList = parseExcelToView(stream, 2);// 打印最后结果 全部数据 不含标题System.out.println(JSONArray.toJSONString(dataList));inputStream.close();}
}
所有数据解析完成!
[{"姓名":"张三0","性别":"男","职称名称":"律师","职称等级":"0 级"},
{"姓名":"张三1","性别":"男","职称名称":"律师","职称等级":"10 级"},
{"姓名":"张三2","性别":"男","职称名称":"律师","职称等级":"20 级"},
{"姓名":"张三3","性别":"男","职称名称":"律师","职称等级":"30 级"},
{"姓名":"张三4","性别":"男","职称名称":"律师","职称等级":"40 级"}]
为了方便后续的操作流程,在解析数据的时候,会将列名作为key!
三、小结
在实际的业务开发过程中,根据参数动态实现 Excel 的导出导入还是非常广的。