diff --git a/blade-starter-excel/pom.xml b/blade-starter-excel/pom.xml
new file mode 100644
index 0000000..ca5d519
--- /dev/null
+++ b/blade-starter-excel/pom.xml
@@ -0,0 +1,47 @@
+
+
+ 4.0.0
+
+ org.springblade
+ blade-tool
+ ${revision}
+
+
+ blade-starter-excel
+ ${project.artifactId}
+ jar
+
+
+ org.springblade.blade.starter.excel
+
+
+
+
+ org.springblade
+ blade-core-launch
+
+
+ cn.idev.excel
+ fastexcel
+
+
+ org.apache.poi
+ poi
+
+
+ org.apache.poi
+ poi-ooxml
+
+
+ org.apache.poi
+ poi-scratchpad
+
+
+ org.apache.poi
+ ooxml-schemas
+
+
+
+
diff --git a/blade-starter-excel/src/main/java/org/springblade/core/excel/listener/DataListener.java b/blade-starter-excel/src/main/java/org/springblade/core/excel/listener/DataListener.java
new file mode 100644
index 0000000..697269c
--- /dev/null
+++ b/blade-starter-excel/src/main/java/org/springblade/core/excel/listener/DataListener.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.excel.listener;
+
+import cn.idev.excel.context.AnalysisContext;
+import cn.idev.excel.event.AnalysisEventListener;
+import lombok.EqualsAndHashCode;
+import lombok.Getter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Excel监听器
+ *
+ * @author Chill
+ */
+@Getter
+@EqualsAndHashCode(callSuper = true)
+public class DataListener extends AnalysisEventListener {
+
+ /**
+ * 缓存的数据列表
+ */
+ private final List dataList = new ArrayList<>();
+
+ @Override
+ public void invoke(T data, AnalysisContext analysisContext) {
+ dataList.add(data);
+ }
+
+ @Override
+ public void doAfterAllAnalysed(AnalysisContext analysisContext) {
+
+ }
+
+}
diff --git a/blade-starter-excel/src/main/java/org/springblade/core/excel/listener/ImportListener.java b/blade-starter-excel/src/main/java/org/springblade/core/excel/listener/ImportListener.java
new file mode 100644
index 0000000..3eedf0d
--- /dev/null
+++ b/blade-starter-excel/src/main/java/org/springblade/core/excel/listener/ImportListener.java
@@ -0,0 +1,71 @@
+/**
+ * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.excel.listener;
+
+import cn.idev.excel.context.AnalysisContext;
+import cn.idev.excel.event.AnalysisEventListener;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.RequiredArgsConstructor;
+import org.springblade.core.excel.support.ExcelImporter;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Excel监听器
+ *
+ * @author Chill
+ */
+@Data
+@RequiredArgsConstructor
+@EqualsAndHashCode(callSuper = true)
+public class ImportListener extends AnalysisEventListener {
+
+ /**
+ * 默认每隔3000条存储数据库
+ */
+ private int batchCount = 3000;
+ /**
+ * 缓存的数据列表
+ */
+ private List list = new ArrayList<>();
+ /**
+ * 数据导入类
+ */
+ private final ExcelImporter importer;
+
+ @Override
+ public void invoke(T data, AnalysisContext analysisContext) {
+ list.add(data);
+ // 达到BATCH_COUNT,则调用importer方法入库,防止数据几万条数据在内存,容易OOM
+ if (list.size() >= batchCount) {
+ // 调用importer方法
+ importer.save(list);
+ // 存储完成清理list
+ list.clear();
+ }
+ }
+
+ @Override
+ public void doAfterAllAnalysed(AnalysisContext analysisContext) {
+ // 调用importer方法
+ importer.save(list);
+ // 存储完成清理list
+ list.clear();
+ }
+
+}
diff --git a/blade-starter-excel/src/main/java/org/springblade/core/excel/support/ExcelException.java b/blade-starter-excel/src/main/java/org/springblade/core/excel/support/ExcelException.java
new file mode 100644
index 0000000..11e492c
--- /dev/null
+++ b/blade-starter-excel/src/main/java/org/springblade/core/excel/support/ExcelException.java
@@ -0,0 +1,36 @@
+/**
+ * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.excel.support;
+
+import java.io.Serial;
+
+/**
+ * Excel异常处理类
+ *
+ * @author Chill
+ */
+public class ExcelException extends RuntimeException {
+ @Serial
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 默认构造函数
+ * @param message 异常信息
+ */
+ public ExcelException(String message) {
+ super(message);
+ }
+}
diff --git a/blade-starter-excel/src/main/java/org/springblade/core/excel/support/ExcelImporter.java b/blade-starter-excel/src/main/java/org/springblade/core/excel/support/ExcelImporter.java
new file mode 100644
index 0000000..7ae014d
--- /dev/null
+++ b/blade-starter-excel/src/main/java/org/springblade/core/excel/support/ExcelImporter.java
@@ -0,0 +1,34 @@
+/**
+ * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.excel.support;
+
+import java.util.List;
+
+/**
+ * Excel统一导入接口
+ *
+ * @author Chill
+ */
+public interface ExcelImporter {
+
+ /**
+ * 导入数据逻辑
+ *
+ * @param data 数据集合
+ */
+ void save(List data);
+
+}
diff --git a/blade-starter-excel/src/main/java/org/springblade/core/excel/util/ExcelUtil.java b/blade-starter-excel/src/main/java/org/springblade/core/excel/util/ExcelUtil.java
new file mode 100644
index 0000000..acd86bf
--- /dev/null
+++ b/blade-starter-excel/src/main/java/org/springblade/core/excel/util/ExcelUtil.java
@@ -0,0 +1,199 @@
+/**
+ * Copyright (c) 2018-2099, Chill Zhuang 庄骞 (bladejava@qq.com).
+ *
+ * Licensed under the GNU LESSER GENERAL PUBLIC LICENSE 3.0;
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.gnu.org/licenses/lgpl.html
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.springblade.core.excel.util;
+
+
+import cn.idev.excel.FastExcel;
+import cn.idev.excel.read.builder.ExcelReaderBuilder;
+import cn.idev.excel.read.listener.ReadListener;
+import cn.idev.excel.util.DateUtils;
+import cn.idev.excel.write.builder.ExcelWriterBuilder;
+import cn.idev.excel.write.handler.WriteHandler;
+import jakarta.servlet.http.HttpServletResponse;
+import lombok.SneakyThrows;
+import org.springblade.core.excel.listener.DataListener;
+import org.springblade.core.excel.listener.ImportListener;
+import org.springblade.core.excel.support.ExcelException;
+import org.springblade.core.excel.support.ExcelImporter;
+import org.springframework.util.StringUtils;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * Excel 工具类
+ *
+ * @author Chill
+ */
+public class ExcelUtil {
+
+ /**
+ * 读取 Excel 的所有 sheet 数据。
+ *
+ * @param 数据模型类型
+ * @param excel Excel 文件
+ * @param clazz 实体类 `Class` 对象
+ * @return 读取到的数据列表
+ */
+ public static List read(MultipartFile excel, Class clazz) {
+ DataListener dataListener = new DataListener<>();
+ ExcelReaderBuilder builder = getReaderBuilder(excel, dataListener, clazz);
+ if (builder == null) {
+ return null;
+ }
+ builder.doReadAll();
+ return dataListener.getDataList();
+ }
+
+ /**
+ * 读取 Excel 指定 sheet 的数据。
+ *
+ * @param 数据模型类型
+ * @param excel Excel 文件
+ * @param sheetNo sheet 序号(从 0 开始)
+ * @param clazz 实体类 `Class` 对象
+ * @return 读取到的数据列表
+ */
+ public static List read(MultipartFile excel, int sheetNo, Class clazz) {
+ return read(excel, sheetNo, 1, clazz);
+ }
+
+ /**
+ * 读取 Excel 指定 sheet 的数据。
+ *
+ * @param 数据模型类型
+ * @param excel Excel 文件
+ * @param sheetNo sheet 序号(从 0 开始)
+ * @param headRowNumber 表头占用的行数
+ * @param clazz 实体类 `Class` 对象
+ * @return 读取到的数据列表
+ */
+ public static List read(MultipartFile excel, int sheetNo, int headRowNumber, Class clazz) {
+ DataListener dataListener = new DataListener<>();
+ ExcelReaderBuilder builder = getReaderBuilder(excel, dataListener, clazz);
+ if (builder == null) {
+ return null;
+ }
+ builder.sheet(sheetNo).headRowNumber(headRowNumber).doRead();
+ return dataListener.getDataList();
+ }
+
+ /**
+ * 读取数据并使用指定的导入器进行数据导入。
+ *
+ * @param 数据模型类型
+ * @param excel Excel 文件
+ * @param importer 导入逻辑处理器
+ * @param clazz 实体类 `Class` 对象
+ */
+ public static void save(MultipartFile excel, ExcelImporter importer, Class clazz) {
+ ImportListener importListener = new ImportListener<>(importer);
+ ExcelReaderBuilder builder = getReaderBuilder(excel, importListener, clazz);
+ if (builder != null) {
+ builder.doReadAll();
+ }
+ }
+
+ /**
+ * 导出 Excel。
+ *
+ * 默认使用当前时间戳作为文件名,"导出数据" 作为 sheet 名。
+ *
+ * @param 数据模型类型
+ * @param response `HttpServletResponse` 对象
+ * @param dataList 要导出的数据列表
+ * @param clazz 实体类 `Class` 对象
+ */
+ @SneakyThrows
+ public static void export(HttpServletResponse response, List dataList, Class clazz) {
+ export(response, DateUtils.format(new Date(), DateUtils.DATE_FORMAT_14), "导出数据", dataList, clazz);
+ }
+
+ /**
+ * 导出 Excel。
+ *
+ * @param 数据模型类型
+ * @param response `HttpServletResponse` 对象
+ * @param fileName 文件名(不含扩展名)
+ * @param sheetName sheet 名称
+ * @param dataList 要导出的数据列表
+ * @param clazz 实体类 `Class` 对象
+ */
+ @SneakyThrows
+ public static void export(HttpServletResponse response, String fileName, String sheetName, List dataList, Class clazz) {
+ export(response, fileName, sheetName, dataList, null, clazz);
+ }
+
+ /**
+ * 导出 Excel。
+ *
+ * @param 数据模型类型
+ * @param response `HttpServletResponse` 对象
+ * @param fileName 文件名(不含扩展名)
+ * @param sheetName sheet 名称
+ * @param dataList 要导出的数据列表
+ * @param writeHandler 自定义 `WriteHandler` 处理器
+ * @param clazz 实体类 `Class` 对象
+ */
+ @SneakyThrows
+ public static void export(HttpServletResponse response, String fileName, String sheetName, List dataList, WriteHandler writeHandler, Class clazz) {
+ response.setContentType("application/vnd.ms-excel");
+ response.setCharacterEncoding(StandardCharsets.UTF_8.name());
+ fileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8);
+ response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
+
+ ExcelWriterBuilder write = FastExcel.write(response.getOutputStream(), clazz);
+ if (writeHandler != null) {
+ write.registerWriteHandler(writeHandler);
+ }
+ write.sheet(sheetName).doWrite(dataList);
+ }
+
+ /**
+ * 获取 Excel 读取构建器 `ExcelReaderBuilder`。
+ *
+ * @param 数据模型类型
+ * @param excel Excel 文件
+ * @param readListener Excel 读取监听器
+ * @param clazz 实体类 `Class` 对象
+ * @return `ExcelReaderBuilder` 构建器实例,若发生 `IOException` 则返回 `null`
+ * @throws ExcelException 如果文件为空或文件类型不正确
+ */
+ public static ExcelReaderBuilder getReaderBuilder(MultipartFile excel, ReadListener readListener, Class clazz) {
+ String filename = excel.getOriginalFilename();
+ if (!StringUtils.hasText(filename)) {
+ throw new ExcelException("请上传文件!");
+ }
+ if ((!StringUtils.endsWithIgnoreCase(filename, ".xls") && !StringUtils.endsWithIgnoreCase(filename, ".xlsx"))) {
+ throw new ExcelException("请上传正确的excel文件!");
+ }
+ InputStream inputStream;
+ try {
+ inputStream = new BufferedInputStream(excel.getInputStream());
+ return FastExcel.read(inputStream, clazz, readListener);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+}
diff --git a/pom.xml b/pom.xml
index a8df3d8..39ede1a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -71,6 +71,7 @@
3.0.1
1.2.0
5.4.1
+ 1.4
@@ -84,6 +85,7 @@
blade-starter-api-crypto
blade-starter-datascope
blade-starter-develop
+ blade-starter-excel
blade-starter-loadbalancer
blade-starter-log
blade-starter-mybatis
@@ -166,6 +168,11 @@
blade-starter-develop
${revision}
+
+ org.springblade
+ blade-starter-excel
+ ${revision}
+
org.springblade
blade-core-secure
@@ -433,12 +440,6 @@
easy-captcha
1.6.2
-
-
- com.alibaba
- easyexcel
- 4.0.3
-
cn.idev.excel
@@ -465,6 +466,11 @@
poi-scratchpad
${poi.version}
+
+ org.apache.poi
+ ooxml-schemas
+ ${poi.ooxl.version}
+
org.bouncycastle
@@ -687,6 +693,8 @@
org.apache.maven.plugins
maven-javadoc-plugin
3.11.2
+
+
package