demoDataList = data1();
+ // 需要表头设置为true,WriteTable一些属性会继承自WriteSheet
+ WriteTable writeTable = EasyExcel.writerTable(1).head(DemoData.class).needHead(Boolean.TRUE)
+ .registerWriteHandler(new CustomMergeStrategy(demoDataList.stream().map(DemoData::getString).collect(Collectors.toList()), 0))
+ .registerWriteHandler(new CustomMergeStrategy(demoDataList.stream().map(o -> o.getDoubleData().toString()).collect(Collectors.toList()), 2))
+ .build();
+ excelWriter.write(demoDataList, writeSheet, writeTable);
+
+ WriteTable writeTable1 = EasyExcel.writerTable(2).head(DemoData.class).needHead(Boolean.TRUE).build();
+ excelWriter.write(data1(), writeSheet, writeTable1);
+ excelWriter.finish();
+ }
+
+}
+
diff --git a/src/main/java/com/pancm/excel/EasyExcelTest.java b/src/main/java/com/pancm/excel/EasyExcelTest.java
new file mode 100644
index 0000000..709c684
--- /dev/null
+++ b/src/main/java/com/pancm/excel/EasyExcelTest.java
@@ -0,0 +1,288 @@
+package com.pancm.excel;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelReader;
+import com.alibaba.excel.context.AnalysisContext;
+import com.alibaba.excel.metadata.CellData;
+import com.alibaba.excel.metadata.CellExtra;
+import com.alibaba.excel.read.listener.ReadListener;
+import com.alibaba.excel.read.metadata.ReadSheet;
+import com.alibaba.fastjson.JSON;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.Test;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author pancm
+ * @Title: pancm_project
+ * @Description: 参考: https://www.yuque.com/easyexcel/doc
+ * @Version:1.0.0
+ * @Since:jdk1.8
+ * @date 2021/1/26
+ */
+@Slf4j
+public class EasyExcelTest {
+
+
+ /**
+ * 最简单的读
+ *
+ * 1. 创建excel对应的实体对象 参照{@link DemoData}
+ *
+ * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
+ *
+ * 3. 直接读即可
+ */
+ @Test
+ public void simpleRead() {
+ // 写法1:JDK8+ ,不用额外写一个DemoDataListener
+ // since: 3.0.0-beta1
+ String fileName = "/home" + "demo" + File.separator + "demo.xlsx";
+ // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
+ // 这里每次会读取3000条数据 然后返回过来 直接调用使用数据就行
+// EasyExcel.read(fileName, DemoData.class, new PageReadListener(dataList -> {
+// for (DemoData demoData : dataList) {
+// log.info("读取到一条数据{}", JSON.toJSONString(demoData));
+// }
+// })).sheet().doRead();
+
+ // 写法2:
+ // 匿名内部类 不用额外写一个DemoDataListener
+ fileName = "/home" + "demo" + File.separator + "demo.xlsx";
+ // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
+ EasyExcel.read(fileName, DemoData.class, new ReadListener() {
+ /**
+ * 单次缓存的数据量
+ */
+ public static final int BATCH_COUNT = 100;
+ /**
+ *临时存储
+ */
+ private List cachedDataList = new ArrayList<>();
+
+ /**
+ * All listeners receive this method when any one Listener does an error report. If an exception is thrown here, the
+ * entire read will terminate.
+ *
+ * @param exception
+ * @param context
+ * @throws Exception
+ */
+ @Override
+ public void onException(Exception exception, AnalysisContext context) throws Exception {
+
+ }
+
+ /**
+ * When analysis one head row trigger invoke function.
+ *
+ * @param headMap
+ * @param context
+ */
+ @Override
+ public void invokeHead(Map headMap, AnalysisContext context) {
+
+ }
+
+ @Override
+ public void invoke(DemoData data, AnalysisContext context) {
+ cachedDataList.add(data);
+ if (cachedDataList.size() >= BATCH_COUNT) {
+ saveData();
+ // 存储完成清理 list
+ cachedDataList = new ArrayList<>();
+ }
+ }
+
+ /**
+ * The current method is called when extra information is returned
+ *
+ * @param extra extra information
+ * @param context
+ */
+ @Override
+ public void extra(CellExtra extra, AnalysisContext context) {
+
+ }
+
+ @Override
+ public void doAfterAllAnalysed(AnalysisContext context) {
+ saveData();
+ }
+
+ /**
+ * Verify that there is another piece of data.You can stop the read by returning false
+ *
+ * @param context
+ * @return
+ */
+ @Override
+ public boolean hasNext(AnalysisContext context) {
+ return false;
+ }
+
+ /**
+ * 加上存储数据库
+ */
+ private void saveData() {
+ log.info("{}条数据,开始存储数据库!", cachedDataList.size());
+ log.info("存储数据库成功!");
+ }
+ }).sheet().doRead();
+
+ // 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
+ // 写法3:
+ fileName = "/home" + "demo" + File.separator + "demo.xlsx";
+ // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
+ EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
+
+ // 写法4:
+ fileName = "/home" + "demo" + File.separator + "demo.xlsx";
+ // 一个文件一个reader
+ ExcelReader excelReader = null;
+ try {
+ excelReader = EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).build();
+ // 构建一个sheet 这里可以指定名字或者no
+ ReadSheet readSheet = EasyExcel.readSheet(0).build();
+ // 读取一个sheet
+ excelReader.read(readSheet);
+ } finally {
+ if (excelReader != null) {
+ // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
+ excelReader.finish();
+ }
+ }
+ }
+
+
+ @Data
+ @EqualsAndHashCode
+ class DemoData {
+ private String string;
+ private Date date;
+ private Double doubleData;
+ }
+
+
+ class DemoDataListener implements ReadListener {
+
+ /**
+ * 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
+ */
+ private static final int BATCH_COUNT = 100;
+ /**
+ * 缓存的数据
+ */
+ private List cachedDataList = new ArrayList<>();
+ /**
+ * 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
+ */
+// private DemoDAO demoDAO;
+//
+// public DemoDataListener() {
+// // 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
+// demoDAO = new DemoDAO();
+// }
+
+ /**
+ * 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
+ *
+ * @param demoDAO
+ */
+// public DemoDataListener(DemoDAO demoDAO) {
+// this.demoDAO = demoDAO;
+// }
+
+ /**
+ * All listeners receive this method when any one Listener does an error report. If an exception is thrown here, the
+ * entire read will terminate.
+ *
+ * @param exception
+ * @param context
+ * @throws Exception
+ */
+ @Override
+ public void onException(Exception exception, AnalysisContext context) throws Exception {
+
+ }
+
+ /**
+ * When analysis one head row trigger invoke function.
+ *
+ * @param headMap
+ * @param context
+ */
+ @Override
+ public void invokeHead(Map headMap, AnalysisContext context) {
+
+ }
+
+ /**
+ * 这个每一条数据解析都会来调用
+ *
+ * @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
+ * @param context
+ */
+ @Override
+ public void invoke(DemoData data, AnalysisContext context) {
+ log.info("解析到一条数据:{}", JSON.toJSONString(data));
+ cachedDataList.add(data);
+ // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
+ if (cachedDataList.size() >= BATCH_COUNT) {
+ saveData();
+ // 存储完成清理 list
+// cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
+ }
+ }
+
+ /**
+ * The current method is called when extra information is returned
+ *
+ * @param extra extra information
+ * @param context
+ */
+ @Override
+ public void extra(CellExtra extra, AnalysisContext context) {
+
+ }
+
+ /**
+ * 所有数据解析完成了 都会来调用
+ *
+ * @param context
+ */
+ @Override
+ public void doAfterAllAnalysed(AnalysisContext context) {
+ // 这里也要保存数据,确保最后遗留的数据也存储到数据库
+ saveData();
+ log.info("所有数据解析完成!");
+ }
+
+ /**
+ * Verify that there is another piece of data.You can stop the read by returning false
+ *
+ * @param context
+ * @return
+ */
+ @Override
+ public boolean hasNext(AnalysisContext context) {
+ return false;
+ }
+
+ /**
+ * 加上存储数据库
+ */
+ private void saveData() {
+ log.info("{}条数据,开始存储数据库!", cachedDataList.size());
+// demoDAO.save(cachedDataList);
+ log.info("存储数据库成功!");
+ }
+ }
+}
diff --git a/src/main/java/com/pancm/excel/EasyExcelUtils.java b/src/main/java/com/pancm/excel/EasyExcelUtils.java
new file mode 100644
index 0000000..281ac49
--- /dev/null
+++ b/src/main/java/com/pancm/excel/EasyExcelUtils.java
@@ -0,0 +1,110 @@
+package com.pancm.excel;
+
+import com.alibaba.excel.EasyExcel;
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.JSONObject;
+import lombok.Data;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.Serializable;
+import java.net.URLEncoder;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class EasyExcelUtils {
+
+ public static void main(String[] args) {
+ String[] headMap = { "项目名称", "楼栋名称", "单元名称", "楼层名称", "房间名称", "业主/租户姓名", "房间状态", "房间功能","认证人数","测试" };
+ String[] dataStrMap={"hName","bName","uName","fName","pName","cName","pState","pFunction","pNum"};
+ NoModelWriteData d = new NoModelWriteData();
+ d.setFileName("认证统计");
+ d.setHeadMap(headMap);
+ d.setDataStrMap(dataStrMap);
+ List listDatas = new ArrayList<>();
+ JSONObject jsonObject = new JSONObject();
+ jsonObject.put("hName","项目1");
+ jsonObject.put("bName","二楼");
+ jsonObject.put("aa","测试");
+ d.setDataList(listDatas);
+ EasyExcelUtils easyExcelUtils = new EasyExcelUtils();
+// easyExcelUtils.jsonWrite(d, response);
+ }
+
+ //不创建对象的导出
+ public void jsonWrite(NoModelWriteData data, HttpServletResponse response) throws IOException {
+ // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
+ try {
+// response.setContentType("application/vnd.ms-excel");
+ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+ response.setCharacterEncoding("utf-8");
+ // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
+ String fileName = URLEncoder.encode(data.getFileName(), "UTF-8");
+ response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
+ // 这里需要设置不关闭流
+ EasyExcel.write(response.getOutputStream()).head(head(data.getHeadMap())).sheet(data.getFileName()).doWrite(dataList(data.getDataList(), data.getDataStrMap()));
+ } catch (Exception e) {
+ // 重置response
+ response.reset();
+ response.setContentType("application/json");
+ response.setCharacterEncoding("utf-8");
+ Map map = new HashMap();
+ map.put("status", "failure");
+ map.put("message", "下载文件失败" + e.getMessage());
+ response.getWriter().println(JSON.toJSONString(map));
+ }
+ }
+
+ //创建对象的导出
+ public void simpleWrite(SimpleWriteData data,Class clazz, HttpServletResponse response) throws IOException {
+ // 这里注意 有同学反应使用swagger 会导致各种问题,请直接用浏览器或者用postman
+// response.setContentType("application/vnd.ms-excel");
+ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
+ response.setCharacterEncoding("utf-8");
+ // 这里URLEncoder.encode可以防止中文乱码 当然和easyexcel没有关系
+ String fileName = URLEncoder.encode(data.getFileName(), "UTF-8");
+ response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
+ EasyExcel.write(response.getOutputStream(), clazz).sheet(data.getFileName()).doWrite(data.getDataList());
+ }
+
+ //设置表头
+ private List> head(String[] headMap) {
+ List> list = new ArrayList>();
+
+ for (String head : headMap) {
+ List headList = new ArrayList();
+ headList.add(head);
+ list.add(headList);
+ }
+ return list;
+ }
+
+ //设置导出的数据内容
+ private List> dataList(List dataList, String[] dataStrMap) {
+ List> list = new ArrayList>();
+ for (JSONObject map : dataList) {
+ List