|
|
@@ -0,0 +1,261 @@
|
|
|
+package shop.alien.util.excel;
|
|
|
+
|
|
|
+import com.alibaba.excel.EasyExcel;
|
|
|
+import com.alibaba.excel.read.listener.ReadListener;
|
|
|
+import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.web.multipart.MultipartFile;
|
|
|
+import shop.alien.util.common.WebTool;
|
|
|
+
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
+import java.io.IOException;
|
|
|
+import java.io.OutputStream;
|
|
|
+import java.net.URLEncoder;
|
|
|
+import java.nio.charset.StandardCharsets;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+/**
|
|
|
+ * EasyExcel 工具类
|
|
|
+ * 封装常用的 Excel 导入导出功能
|
|
|
+ *
|
|
|
+ * @author system
|
|
|
+ * @since 2025-01-XX
|
|
|
+ */
|
|
|
+@Slf4j
|
|
|
+public class EasyExcelUtil {
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出 Excel 文件到 HTTP 响应(自动获取响应对象)
|
|
|
+ *
|
|
|
+ * @param dataList 要导出的数据列表
|
|
|
+ * @param clazz 数据类型的 Class 对象
|
|
|
+ * @param fileName 导出的文件名(不带扩展名)
|
|
|
+ * @param sheetName 工作表名称
|
|
|
+ * @param <T> 数据类型
|
|
|
+ * @throws IOException 如果发生 IO 异常
|
|
|
+ */
|
|
|
+ public static <T> void exportExcel(List<T> dataList, Class<T> clazz, String fileName, String sheetName) throws IOException {
|
|
|
+ HttpServletResponse response = WebTool.getResponse();
|
|
|
+ exportExcel(response, dataList, clazz, fileName, sheetName);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出 Excel 文件到 HTTP 响应
|
|
|
+ *
|
|
|
+ * @param response HttpServletResponse 对象
|
|
|
+ * @param dataList 要导出的数据列表
|
|
|
+ * @param clazz 数据类型的 Class 对象
|
|
|
+ * @param fileName 导出的文件名(不带扩展名)
|
|
|
+ * @param sheetName 工作表名称
|
|
|
+ * @param <T> 数据类型
|
|
|
+ * @throws IOException 如果发生 IO 异常
|
|
|
+ */
|
|
|
+ public static <T> void exportExcel(HttpServletResponse response, List<T> dataList, Class<T> clazz,
|
|
|
+ String fileName, String sheetName) throws IOException {
|
|
|
+ if (dataList == null || dataList.isEmpty()) {
|
|
|
+ log.warn("[EasyExcelUtil] 导出数据为空: {}", fileName);
|
|
|
+ throw new IllegalArgumentException("导出数据为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 设置响应头
|
|
|
+ setupResponse(response, fileName);
|
|
|
+
|
|
|
+ // 导出Excel
|
|
|
+ EasyExcel.write(response.getOutputStream(), clazz)
|
|
|
+ .sheet(sheetName != null ? sheetName : "Sheet1")
|
|
|
+ .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 自动列宽
|
|
|
+ .doWrite(dataList);
|
|
|
+
|
|
|
+ log.info("[EasyExcelUtil] 导出成功: {}, 共 {} 条数据", fileName, dataList.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出 Excel 文件到 HTTP 响应(使用默认工作表名称)
|
|
|
+ *
|
|
|
+ * @param response HttpServletResponse 对象
|
|
|
+ * @param dataList 要导出的数据列表
|
|
|
+ * @param clazz 数据类型的 Class 对象
|
|
|
+ * @param fileName 导出的文件名(不带扩展名)
|
|
|
+ * @param <T> 数据类型
|
|
|
+ * @throws IOException 如果发生 IO 异常
|
|
|
+ */
|
|
|
+ public static <T> void exportExcel(HttpServletResponse response, List<T> dataList, Class<T> clazz, String fileName) throws IOException {
|
|
|
+ exportExcel(response, dataList, clazz, fileName, "Sheet1");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出 Excel 文件到本地文件
|
|
|
+ *
|
|
|
+ * @param filePath 文件路径
|
|
|
+ * @param dataList 要导出的数据列表
|
|
|
+ * @param clazz 数据类型的 Class 对象
|
|
|
+ * @param sheetName 工作表名称
|
|
|
+ * @param <T> 数据类型
|
|
|
+ * @throws IOException 如果发生 IO 异常
|
|
|
+ */
|
|
|
+ public static <T> void exportExcelToFile(String filePath, List<T> dataList, Class<T> clazz, String sheetName) throws IOException {
|
|
|
+ if (dataList == null || dataList.isEmpty()) {
|
|
|
+ log.warn("[EasyExcelUtil] 导出数据为空: {}", filePath);
|
|
|
+ throw new IllegalArgumentException("导出数据为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ EasyExcel.write(filePath, clazz)
|
|
|
+ .sheet(sheetName != null ? sheetName : "Sheet1")
|
|
|
+ .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
|
|
|
+ .doWrite(dataList);
|
|
|
+
|
|
|
+ log.info("[EasyExcelUtil] 导出到文件成功: {}, 共 {} 条数据", filePath, dataList.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出 Excel 文件到本地文件(使用默认工作表名称)
|
|
|
+ *
|
|
|
+ * @param filePath 文件路径
|
|
|
+ * @param dataList 要导出的数据列表
|
|
|
+ * @param clazz 数据类型的 Class 对象
|
|
|
+ * @param <T> 数据类型
|
|
|
+ * @throws IOException 如果发生 IO 异常
|
|
|
+ */
|
|
|
+ public static <T> void exportExcelToFile(String filePath, List<T> dataList, Class<T> clazz) throws IOException {
|
|
|
+ exportExcelToFile(filePath, dataList, clazz, "Sheet1");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导出 Excel 文件到输出流
|
|
|
+ *
|
|
|
+ * @param outputStream 输出流
|
|
|
+ * @param dataList 要导出的数据列表
|
|
|
+ * @param clazz 数据类型的 Class 对象
|
|
|
+ * @param sheetName 工作表名称
|
|
|
+ * @param <T> 数据类型
|
|
|
+ * @throws IOException 如果发生 IO 异常
|
|
|
+ */
|
|
|
+ public static <T> void exportExcelToStream(OutputStream outputStream, List<T> dataList, Class<T> clazz, String sheetName) throws IOException {
|
|
|
+ if (dataList == null || dataList.isEmpty()) {
|
|
|
+ log.warn("[EasyExcelUtil] 导出数据为空");
|
|
|
+ throw new IllegalArgumentException("导出数据为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ EasyExcel.write(outputStream, clazz)
|
|
|
+ .sheet(sheetName != null ? sheetName : "Sheet1")
|
|
|
+ .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
|
|
|
+ .doWrite(dataList);
|
|
|
+
|
|
|
+ log.info("[EasyExcelUtil] 导出到流成功, 共 {} 条数据", dataList.size());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导入 Excel 文件
|
|
|
+ *
|
|
|
+ * @param file 上传的 MultipartFile 文件
|
|
|
+ * @param clazz 数据类型的 Class 对象
|
|
|
+ * @param readListener 读取监听器
|
|
|
+ * @param <T> 数据类型
|
|
|
+ * @throws IOException 如果发生 IO 异常
|
|
|
+ */
|
|
|
+ public static <T> void importExcel(MultipartFile file, Class<T> clazz, ReadListener<T> readListener) throws IOException {
|
|
|
+ if (file == null || file.isEmpty()) {
|
|
|
+ throw new IllegalArgumentException("上传文件为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("[EasyExcelUtil] 开始导入Excel文件: {}", file.getOriginalFilename());
|
|
|
+
|
|
|
+ EasyExcel.read(file.getInputStream(), clazz, readListener)
|
|
|
+ .sheet()
|
|
|
+ .doRead();
|
|
|
+
|
|
|
+ log.info("[EasyExcelUtil] Excel文件导入完成: {}", file.getOriginalFilename());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导入 Excel 文件(指定Sheet)
|
|
|
+ *
|
|
|
+ * @param file 上传的 MultipartFile 文件
|
|
|
+ * @param clazz 数据类型的 Class 对象
|
|
|
+ * @param readListener 读取监听器
|
|
|
+ * @param sheetNo Sheet索引(从0开始)
|
|
|
+ * @param <T> 数据类型
|
|
|
+ * @throws IOException 如果发生 IO 异常
|
|
|
+ */
|
|
|
+ public static <T> void importExcel(MultipartFile file, Class<T> clazz, ReadListener<T> readListener, int sheetNo) throws IOException {
|
|
|
+ if (file == null || file.isEmpty()) {
|
|
|
+ throw new IllegalArgumentException("上传文件为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("[EasyExcelUtil] 开始导入Excel文件: {}, Sheet: {}", file.getOriginalFilename(), sheetNo);
|
|
|
+
|
|
|
+ EasyExcel.read(file.getInputStream(), clazz, readListener)
|
|
|
+ .sheet(sheetNo)
|
|
|
+ .doRead();
|
|
|
+
|
|
|
+ log.info("[EasyExcelUtil] Excel文件导入完成: {}", file.getOriginalFilename());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 导入 Excel 文件(指定Sheet名称)
|
|
|
+ *
|
|
|
+ * @param file 上传的 MultipartFile 文件
|
|
|
+ * @param clazz 数据类型的 Class 对象
|
|
|
+ * @param readListener 读取监听器
|
|
|
+ * @param sheetName Sheet名称
|
|
|
+ * @param <T> 数据类型
|
|
|
+ * @throws IOException 如果发生 IO 异常
|
|
|
+ */
|
|
|
+ public static <T> void importExcel(MultipartFile file, Class<T> clazz, ReadListener<T> readListener, String sheetName) throws IOException {
|
|
|
+ if (file == null || file.isEmpty()) {
|
|
|
+ throw new IllegalArgumentException("上传文件为空");
|
|
|
+ }
|
|
|
+
|
|
|
+ log.info("[EasyExcelUtil] 开始导入Excel文件: {}, Sheet: {}", file.getOriginalFilename(), sheetName);
|
|
|
+
|
|
|
+ EasyExcel.read(file.getInputStream(), clazz, readListener)
|
|
|
+ .sheet(sheetName)
|
|
|
+ .doRead();
|
|
|
+
|
|
|
+ log.info("[EasyExcelUtil] Excel文件导入完成: {}", file.getOriginalFilename());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 下载导入模板(只有表头,无数据)
|
|
|
+ *
|
|
|
+ * @param response HttpServletResponse 对象
|
|
|
+ * @param clazz 数据类型的 Class 对象
|
|
|
+ * @param fileName 文件名(不带扩展名)
|
|
|
+ * @param <T> 数据类型
|
|
|
+ * @throws IOException 如果发生 IO 异常
|
|
|
+ */
|
|
|
+ public static <T> void downloadTemplate(HttpServletResponse response, Class<T> clazz, String fileName) throws IOException {
|
|
|
+ setupResponse(response, fileName);
|
|
|
+
|
|
|
+ // 导出空列表,只有表头
|
|
|
+ EasyExcel.write(response.getOutputStream(), clazz)
|
|
|
+ .sheet("Sheet1")
|
|
|
+ .registerWriteHandler(new LongestMatchColumnWidthStyleStrategy())
|
|
|
+ .doWrite(new java.util.ArrayList<>());
|
|
|
+
|
|
|
+ log.info("[EasyExcelUtil] 模板下载成功: {}", fileName);
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 设置 HTTP 响应头
|
|
|
+ *
|
|
|
+ * @param response HttpServletResponse 对象
|
|
|
+ * @param fileName 文件名(不带扩展名)
|
|
|
+ * @throws IOException 如果发生 IO 异常
|
|
|
+ */
|
|
|
+ private static void setupResponse(HttpServletResponse response, String fileName) throws IOException {
|
|
|
+ response.reset(); // 重置响应,清除之前可能设置的响应头
|
|
|
+ response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
|
|
+ response.setCharacterEncoding("utf-8");
|
|
|
+
|
|
|
+ // 使用 RFC 5987 标准格式,更好地支持中文文件名
|
|
|
+ String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8.toString()).replaceAll("\\+", "%20");
|
|
|
+ response.setHeader("Content-Disposition", "attachment;filename=\"" + encodedFileName + ".xlsx\";filename*=utf-8''" + encodedFileName + ".xlsx");
|
|
|
+
|
|
|
+ // 设置缓存控制
|
|
|
+ response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
|
|
|
+ response.setHeader("Pragma", "no-cache");
|
|
|
+ response.setDateHeader("Expires", 0);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|